Now that I am parsing pattern synonym declarations using the pattern parser (using the same trick as used by the data constructor parser), and typechecking a pattern synonym can add deferred bindings, this should at least be somewhat simpler to implement.
I just ran into this issue. I'm trying to generalize a legacy record type like
data Foo = Foo{bar :: String}
to
data GenFoo s = GenFoo{bar :: s}
Unfortunately there is no way to provide a backward-compatible interface after this change. I'm hoping with this feature the solution might be as simple as
type Foo = GenFoo Stringpattern Foo{bar} = GenFoo bar
The first example is a bit tricky although I don't think the results are unexpected.
If instead you write
data D = MkD { foo :: Int }pattern Pat = MkD { foo = 6 }baz = (Pat) { foo = 5 }
then everything works as expected. Otherwise there is a type error as Pat does not have a field foo. This is similar to one of the examples I posted on the original discussion thread.
how, exactly should pattern matches on Foo expand? I can't tell.
Another way to get at this question would be to look at Section 3.17 of the langauge definition. What changes, precisely, are required to accommodate pattern synonyms? (It's a deficiency in the current user manual that it does not say.)
To give the idea, here is my first stab at extending 3.17.2. Add an extra item to the list, saying
To match a pattern P p1 .. pn against a value, where P is a pattern synonym defined by pattern P x1 .. xn = prhs,
* first match the pattern prhs against the value, thereby binding x1..xn.
* and then match p1 agianst x1, p2 against x2, and so on in sequence.
A similar modification to 3.17.3 would be needed.
Now, how would you change those words if your proposal was adopted?
I think the confusion here is that pattern synonyms is an undescriptive name for the feature. I prefer to think of (bidirectional) pattern synonyms as data constructors which are not (yet) associated with a particular type.
It is better then to say that instead of "In general, a pattern synonym should behave exactly like its expansion." that "In general, a pattern synonym should behave exactly like the relevant data constructor". For example, a bidirectional prefix pattern synonym should behave like a prefix data constructor, an infix pattern synonym should behave like an infix data constructor and a record pattern synonym should behave like a record data constructor.
When we introduce records the expansion idea falls apart a bit. For normal prefix pattern synonyms there is one way to pattern match and one way to construct (which matches the expansion). With records there are a few ways to pattern match, a few ways to construct and also the possibility to update. This means the syntax has to diverge from the expansion idea as which expansion do we choose?
If we define a synonym P and datatype Q as follows,
then we expect MkP to behave precisely as MkQ modulo field naming. To be clear these situations are as follows.
Construction (MkP 0 0)
Construction with record syntax (MkP { x = 0, y = 1 })
Matching (foo (MkP c l) = ...)
Matching with normal record syntax (foo (MkP {x = l, y = c}) = ...)
Matching with field puns (foo (MkP {x, y}) = ...)
Updating with record syntax ((MkP 0 0) { x = 1 })
Using record selectors (x (MkP 0 0))
For a unidirectional synonym, we define selectors and the matching part but not updates or construction.
Is that clearer? I think the best specification for this feature is in terms of ordinary records as the goal is to get as close as possible to normal record data constructors.
Matthew, I feel bad about this but I still don't understand the specification. I honestly don't know what it means to say "a pattern synonym should behave like the relevant data constructor". The wiki page does not even give a syntax. I think it may be something like this
patsyndecl ::= 'pattern' con var1 .. varn <- pat | 'pattern' con '{' var1 ',' ... ',' varn '}' <- pat | ... more for bidirectional ...
where the second line is the new bit. Is that right? Just writing out the syntax would be very helpful.
We need semantics as well as syntax. In ticket:8582#comment:107111 I tried to give some concrete pointers for what a specification might look like. It has to say
What the syntax is
What it means to use such a pattern synonym as a constructor
What it means to match against such a synonym
I don't think any of this is very hard to do. But until it is done I don't know what the feature is, so it's hard to review the implementation.
On the implementation front, I believe that you are stuck on a particular point. There's a long comment stream on the Phab ticket so I'm not sure what you are stuck on. Can you just identify the sticking point?
Simon, I didn't want to write an explicit specification for this patch because it would amount to copying the specification for records.
Which bit did you find confusing in the example I gave? In retrospect, the phrase "relevant data constructor" is confusing. So to explain, by "relevant" I mean an isomorphic (normally defined) data constructor. Does the example not make this any clearer?
The problem with the implementation is with the record updates. Pattern synonym builders have required constraints which normal data constructors don't have. When the record update is desugared, it is necessary to provide the dictionaries for these constraints to the builder. My question was, how was I meant to do this. I ended up adding a field to the RecordUpd constructor which carried around the HsWrapper which then applied the dictionaries. More details are to be found in the last comment on the phab ticket.
I don't have much time anymore to work on this ticket but I would be very disappointed it if did make it into GHC 8.0 as I started working on it several months ago.