Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Factoring out common constants in pattern synonym code?

I had some code like this:

newtype T = T Text

pattern Alice = T "Alice" 

This is fine, but I was using "Alice" in other places. So I decided to factor it out like so:

alice :: Text
alice = "Alice"

pattern Alice = T alice

But when I built this with warnings on, I got a warning about an unused variable.

I then realised that:

pattern Alice = T alice

actually matches everything, as opposed just T "Alice"

It then seemed weird that even T "Alice" was allowed, as "Alice" is Text, something which is computed.

But then I saw on the Overloaded Strings docs that:

String literals behave very much like integer literals, i.e., they can be used in both expressions and patterns. If used in a pattern the literal will be replaced by an equality test, in the same way as an integer literal is.

So this raises a few questions:

  1. Could I even write the pattern Alice without enabling the overloaded strings extension?
  2. Can I create pattern synonyms where the RHS requires some computation, and have GHC use Eq to match, just like it does for numeric and string literals? Or are numeric and string literals a special case and GHC doesn't allow one to generalise that functionality?
like image 345
Clinton Avatar asked Dec 30 '25 21:12

Clinton


2 Answers

You can use view patterns to perform any computation in the pattern and then use the result to match anything else.

ghci> :set -XPatternSynonyms
ghci> :set -XViewPatterns
alice = "Alice"
ghci> pattern Alice <- ((==alice) -> True)

Here you apply (==alice) to the argument and match the result against True.

like image 112
n. 1.8e9-where's-my-share m. Avatar answered Jan 02 '26 12:01

n. 1.8e9-where's-my-share m.


Could I even write the pattern Alice without enabling the overloaded strings extension?

No, since you need to OverloadedStrings to also adapt the string literals in patterns. If you don't, then it will not work, or at least not without some unpacking. Behind the curtains it will use fromString on the value of the pattern and check equivalence, so:

f "Alice" = "foo"
f _ = "bar"

is essentially equivalent to:

f x | x == fromString "Alice" = "foo"
f _ = "bar"

As for factoring out subpatterns, why not just make an AliceText pattern:

pattern AliceText :: Text
pattern AliceText = "Alice"

pattern Alice = T AliceText

and thus create different pattern synonyms that can refer to each other. Furthermore a pattern can also be used as expression here, so AliceText can be used wherever you need Text with "Alice" as value.

like image 43
Willem Van Onsem Avatar answered Jan 02 '26 12:01

Willem Van Onsem