Pattern Synonyms in GHC 8.0

Posted on December 12, 2015

There have been four small but significant improvements to pattern synonyms which are going to appear in GHC 8.0.

This work closes up some holes which were left in the implementation of pattern synonyms and should provide library authors with a new and flexible method of abstraction.

More information about pattern synonyms can be found in the GHC 8.0 user guide.

Record Pattern Synonyms

The biggest update extends pattern synonyms to allow the construction of pattern synonyms which behave like record data constructors.

Since GHC 7.8 you have been able to define prefix and infix pattern synonyms which behave like normal data constructors. With the addition of record pattern synonyms most data constructors can be replicated by pattern synonyms.

To make this clear, consider the data constructor Just . We can use this constructor in two contexts, in a pattern match or in an expression context to construct a value.

If we defined the pattern synonym MyJust , we can use it in precisely the same contexts as Just .

Similarly, record data constructors can be used in seven contexts.

Usage Example As a constructor zero = Point 0 0 As a constructor with record syntax zero = Point { x = 0, y = 0} In a pattern context isZero (Point 0 0) = True In a pattern context with record syntax isZero (Point { x = 0, y = 0 } In a pattern context with field puns getX (Point {x}) = x In a record update (0, 0) { x = 1 } == (1,0) Using record selectors x (0,0) == 0

Record pattern synonyms are defined as follows and can also be used in these seven contexts.

Projection functions, x and y are defined like record selectors for ordinary constructors.

Because we defined this pattern synonym, tuples can now be updated with record update syntax.

Bundling Pattern Synonyms

Since pattern synonyms are a lot like data constructors, they should be able to be imported just like data constructors. To put it another way a user should be unaware whether they are using a pattern synonym or a data constructor.

However, before GHC 8.0, there has been quite an awkward distinction between the two. Data constructors couldn’t be imported or exported separated from the type which they construct. On the other hand, pattern synonyms could only be imported and exported individually by using the pattern keyword. This meant that consumers had to be aware that whether they were importing a pattern synonym or not! No good!

Now there are two ways which we can export the pattern synonym P :: A .

Separately, as before, by using the pattern keyword. haskell module Foo (pattern P) where Bundled with the relevant type constructor haskell module Foo ( A(P) ) where or to export all of A ’s constructors along with the pattern synonym P . haskell module Foo ( A(.., P) ) where

In this second case, if another module imports Foo then P can be imported alongwith A .

or

An Example: ErrorCall

This problem reared its head in one of the first serious uses of pattern synonyms. In GHC 8 a pattern synonym ErrorCall is introduced into the base library to smooth over changes in the internal representation caused by Eric Seidel’s work on call stacks.

The datatype ErrorCall previously just had one synonymous constructor.

After the refactoring, the single constructor was renamed to ErrorCallWithLocation but Eric wanted to smooth over the transition by providing a pattern synonym which would behave much like before.

However clients importing ErrorCall(..) found that despite the careful efforts of the library author this change broke their code. The problem being that by default, it is necessary to explicitly import pattern synonyms.

With this feature, we can now bundle the new ErrorCall pattern synonym in the export list of the module so that users importing ErrorCall(..) will also import the pattern synonym.

Pattern Synonym Signatures

Pattern synonyms can also have type signatures. The syntax is very similar to normal type signatures but there are two sets of constraints rather than the usual one which correspond to “required” and “provided” constraints.

In the common case that there are no provided constraints, it is possible to omit the first set of constraints.

and in the even more common case when there are no constraints, both can be omitted.

Required constraints are constraints which are required in order to make a match. For example, we could provide the quite silly pattern synonym which uses show to check whether a pattern should match. As show is from the Show typeclass, we have to add it to the required constraints.

Provided constraints are constraints which are made available on a successful match. This usually occurs when matching on a GADT with an existential type.

In fact, it only makes sense for provided constraints to mention existentially quantified type variables which explains why they are less often used.

Pattern synonym signatures aren’t new for GHC 8.0 but the order of required and provided constraints has been switched.

Warnings for missing pattern synonym signatures