[GHC] #8582: Record syntax for pattern synonyms
#8582: Record syntax for pattern synonyms ------------------------------------+------------------------------------- Reporter: cactus | Owner: cactus Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Keywords: | Operating System: Unknown/Multiple Architecture: Unknown/Multiple | Type of failure: None/Unknown Difficulty: Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | ------------------------------------+------------------------------------- Implement syntax for {{{ pattern Cons{car, cdr} = (car, cdr) }}} which can then be used just like a regular record constructor, so the following are all valid patterns: {{{ Cons (Just x) [] Cons{car = Just x} }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------ Reporter: cactus | Owner: cactus Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Unknown/Multiple Type of failure: None/Unknown | Difficulty: Unknown Test Case: | Blocked By: 5144 Blocking: | Related Tickets: -------------------------------------+------------------------------------ Comment (by cactus): 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. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:1> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: cactus Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: Operating System: Unknown/Multiple | Architecture: Type of failure: None/Unknown | Unknown/Multiple Blocked By: 5144 | Test Case: Related Tickets: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by blamario): 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 String pattern Foo{bar} = GenFoo bar }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:2> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: cactus Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: Operating System: Unknown/Multiple | PatternSynonyms Type of failure: None/Unknown | Architecture: Blocked By: 5144 | Unknown/Multiple Related Tickets: | Test Case: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- Changes (by cactus): * keywords: => PatternSynonyms -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:3> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: cactus Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: Operating System: Unknown/Multiple | PatternSynonyms Type of failure: None/Unknown | Architecture: Blocked By: 5144 | Unknown/Multiple Related Tickets: | Test Case: | Blocking: | Differential Revisions: -------------------------------------+------------------------------------- Comment (by mpickering): I was also thinking this would be nice and would be interested to implement it before 7.12. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:4> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: -------------------------------------+------------------------------------- Changes (by mpickering): * owner: cactus => mpickering -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:5> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: normal | Milestone: Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Changes (by mpickering): * differential: => Phab:D1152 -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:6> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 7.12.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Changes (by simonpj): * priority: normal => high * milestone: => 7.12.1 Comment: Matthew says he thinks this'll be ready for 7.12 so I'll milestone it as such. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:7> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Comment (by goldfire): Some idle thought has spurred on some questions about the design of this feature. See [https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#Trickybits a new section on the wiki page]. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:9> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Comment (by mpickering): 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. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:10> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Comment (by simonpj): I'm sorry but I'm honestly not sure what the specification of the new feature is. The [wiki:PatternSynonyms#Design design subsection] is helpful, but it uses only one example, and I can't tell what is supposed to happen in general. Let's try this. In general, a pattern synonym should behave exactly like its expansion. So given {{{ pattern P x = (x, True) }}} these two functions should behave the same: {{{ f1 (P False) = "yes" f2 (P True) = "no" f2 (False,True) = "yes" -- Replace (P pat) by its expansion f2 (True, True) = "no" }}} (Actually they are not quite the same; see [wiki:PatternSynonyms#DynamicSemantics dynamic semantics subsection]. But close enough for now.) But for your proposal, given {{{ pattern Foo{bar, baz} = (bar, baz) }}} how, exactly should pattern matches on `Foo` expand? I can't tell. Another way to get at this question would be to look at [https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-580003.1... 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? Thanks. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:11> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Comment (by mpickering): 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, {{{#!hs pattern MkP :: Int -> Int -> Q pattern MkP{x, y} = MkQ x1 y1 data Q = MkQ { x1 :: Int, y1 :: Int } }}} 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. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:12> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Revisions: Phab:D1152 -------------------------------------+------------------------------------- Comment (by goldfire): Replying to [comment:12 mpickering]:
{{{#!hs
pattern MkP :: Int -> Int -> Q pattern MkP{x, y} = MkQ x1 y1
data Q = MkQ { x1 :: Int, y1 :: Int } }}}
I imagine you meant {{{#!hs pattern MkP{x, y} = MkQ x y }}} Otherwise, I agree with what you've said above. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:13> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Rev(s): Phab:D1152 -------------------------------------+------------------------------------- Comment (by simonpj): 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:PatternSynonyms#Design 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 comment:11 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? -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:14> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: new Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Rev(s): Phab:D1152 -------------------------------------+------------------------------------- Comment (by mpickering): 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. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:15> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: closed Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: fixed | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Rev(s): Phab:D1152 Wiki Page: | -------------------------------------+------------------------------------- Changes (by bgamari): * status: new => closed * resolution: => fixed Comment: Record pattern synonym support has been merged. Moreover, a specification for the implemented syntax can be found on [[PatternSynonyms/RecordPatternSynonyms]]. -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:16> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
#8582: Record syntax for pattern synonyms -------------------------------------+------------------------------------- Reporter: cactus | Owner: mpickering Type: feature request | Status: closed Priority: high | Milestone: 8.0.1 Component: Compiler | Version: Resolution: fixed | Keywords: | PatternSynonyms Operating System: Unknown/Multiple | Architecture: | Unknown/Multiple Type of failure: None/Unknown | Test Case: Blocked By: 5144 | Blocking: Related Tickets: | Differential Rev(s): Phab:D1152 Wiki Page: | -------------------------------------+------------------------------------- Comment (by thomie): commit 2a74a64e8329ab9e0c74bec47198cb492d25affb: {{{ Author: Matthew Pickering <matthewtpickering@gmail.com> Date: Mon Oct 19 21:17:29 2015 +0100 Record pattern synonyms This patch implements an extension to pattern synonyms which allows user to specify pattern synonyms using record syntax. Doing so generates appropriate selectors and update functions. === Interaction with Duplicate Record Fields === The implementation given here isn't quite as general as it could be with respect to the recently-introduced `DuplicateRecordFields` extension. Consider the following module: {-# LANGUAGE DuplicateRecordFields #-} {-# LANGUAGE PatternSynonyms #-} module Main where pattern S{a, b} = (a, b) pattern T{a} = Just a main = do print S{ a = "fst", b = "snd" } print T{ a = "a" } In principle, this ought to work, because there is no ambiguity. But at the moment it leads to a "multiple declarations of a" error. The problem is that pattern synonym record selectors don't do the same name mangling as normal datatypes when DuplicateRecordFields is enabled. They could, but this would require some work to track the field label and selector name separately. In particular, we currently represent datatype selectors in the third component of AvailTC, but pattern synonym selectors are just represented as Avails (because they don't have a corresponding type constructor). Moreover, the GlobalRdrElt for a selector currently requires it to have a parent tycon. (example due to Adam Gundry) === Updating Explicitly Bidirectional Pattern Synonyms === Consider the following ``` pattern Silly{a} <- [a] where Silly a = [a, a] f1 = a [5] -- 5 f2 = [5] {a = 6} -- currently [6,6] ``` === Fixing Polymorphic Updates === They were fixed by adding these two lines in `dsExpr`. This might break record updates but will be easy to fix. ``` + ; let req_wrap = mkWpTyApps (mkTyVarTys univ_tvs) - , pat_wrap = idHsWrapper } +, pat_wrap = req_wrap } ``` === Mixed selectors error === Note [Mixed Record Field Updates] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Consider the following pattern synonym. data MyRec = MyRec { foo :: Int, qux :: String } pattern HisRec{f1, f2} = MyRec{foo = f1, qux=f2} This allows updates such as the following updater :: MyRec -> MyRec updater a = a {f1 = 1 } It would also make sense to allow the following update (which we reject). updater a = a {f1 = 1, qux = "two" } ==? MyRec 1 "two" This leads to confusing behaviour when the selectors in fact refer the same field. updater a = a {f1 = 1, foo = 2} ==? ??? For this reason, we reject a mixture of pattern synonym and normal record selectors in the same update block. Although of course we still allow the following. updater a = (a {f1 = 1}) {foo = 2} > updater (MyRec 0 "str") MyRec 2 "str" }}} -- Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/8582#comment:17> GHC <http://www.haskell.org/ghc/> The Glasgow Haskell Compiler
participants (1)
-
GHC