Record syntax for pattern synonyms

I was looking at implementing #8582 but before I got too far I thought it best to clear up a few design points. A summary can be found below and a more fleshed out version with some examples can be found on the wiki page[1]. My main question is about how best to deal with record updates. Say that Foo is a record pattern synonym then how would we expect the following program to behave? ``` foo a@Foo{..} = a {bar = baz} ``` Then say that `pattern Foo{bar} = Just bar`, how should the following two programs behave? Is this partiality any different to that caused by ordinary use of pattern synonyms? (At least partiality in patterns is warned but how comprehensive is the coverage?) ``` foo :: Maybe a -> Maybe Int foo x = x {bar = 5} -- error as `bar` unique determines that we need Foo bar = Nothing {bar = 5} ``` Abandoning record updates seems to make record syntax for pattern synonyms far less useful and confusing to users. Is this design how others have imagined it? I have cced Gergő who originally implemented the extension and created #8582. Matt ---- Unidirectional patterns * Provide the same ability to match as normal records (RecordWildcards etc) * Provide selector functions Bidirectional patterns * Provide the constructor which can be used as normal record constructors Record Updates - unclear * Generalise update syntax to arbitrary expressions? [1]: https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#RecordPatternSynonyms

On Aug 10, 2015, at 7:10 PM, Matthew Pickering
My main question is about how best to deal with record updates. Say that Foo is a record pattern synonym then how would we expect the following program to behave?
``` foo a@Foo{..} = a {bar = baz} ```
I'm assuming `pattern Foo{bar, baz} = (bar, baz)` from the wiki page, without any further pattern type signature. This example then looks straightforward to me -- I feel I'm missing the subtlety. `foo` would get the type `(a,b) -> (b,b)` and would be roughly equivalent to `foo a@(bar, baz) = case a of (_, baz2) -> (baz, baz2)`. The case statement and baz2 is necessary just to provide a predictable desugaring of record updates; handwritten code should clearly be more succinct.
Then say that `pattern Foo{bar} = Just bar`, how should the following two programs behave? Is this partiality any different to that caused by ordinary use of pattern synonyms?
Did you mean "pattern synonyms" --> "record updates"?
(At least partiality in patterns is warned but how comprehensive is the coverage?)
``` foo :: Maybe a -> Maybe Int foo x = x {bar = 5}
This would desugar to `foo x = case x of Just _ -> Just 5`. I'm not sure about pattern exhaustiveness warnings, but I would expect such a record update to be partial. The partiality of record updates has been surprising in the past, but I don't think adding pattern synonyms to the mix should change that.
-- error as `quux` unique determines that we need Foo quux = Nothing {bar = 5}
This (renamed example) does not compile, as GHC would parse this as a record-creation expression using the Nothing constructor, which does not have record fields. On the other hand
quux = (Nothing) {bar = 5}
*would* compile. (This is no different than record updates today.) It would desugar to `quux = case Nothing of Just _ -> Just 5` and `quux` would have type `Maybe Int` (assuming `5 :: Int`). Evaluating `quux` would clearly fail.
```
Abandoning record updates seems to make record syntax for pattern synonyms far less useful and confusing to users. Is this design how others have imagined it? I have cced Gergő who originally implemented the extension and created #8582.
I would like to keep record updates for the same reasons you appear to. I will warn that they are quite hard to work with, though! About 220 lines of dense code (including comments) are necessary to type-check regular old record updates. This isn't to scare you off, but to have you suitably forewarned and forearmed.
Matt
----
Unidirectional patterns * Provide the same ability to match as normal records (RecordWildcards etc) * Provide selector functions
Bidirectional patterns * Provide the constructor which can be used as normal record constructors
Record Updates - unclear * Generalise update syntax to arbitrary expressions?
What do you mean here? Without checking, I assumed that the x in `x { ... }` had to be a variable. But this is wrong! See 3.15.3 of the Haskell 2010 report (https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-490003.1...). So I think it's already generalized. Many thanks for taking this on! Richard
[1]: https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#RecordPatternSynonyms _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

Thank you for your comments Richard.
I'm assuming `pattern Foo{bar, baz} = (bar, baz)` from the wiki page, without any further pattern type signature. This example then looks straightforward to me -- I feel I'm missing the subtlety. `foo` would get the type `(a,b) -> (b,b)` and would be roughly equivalent to `foo a@(bar, baz) = case a of (_, baz2) -> (baz, baz2)`. The case statement and baz2 is necessary just to provide a predictable desugaring of record updates; handwritten code should clearly be more succinct.
This is how I imagined it to work.
This would desugar to `foo x = case x of Just _ -> Just 5`. I'm not sure about pattern exhaustiveness warnings, but I would expect such a record update to be partial. The partiality of record updates has been surprising in the past, but I don't think adding pattern synonyms to the mix should change that.
Yes, I agree.
I would like to keep record updates for the same reasons you appear to. I will warn that they are quite hard to work with, though! About 220 lines of dense code (including comments) are necessary to type-check regular old record updates. This isn't to scare you off, but to have you suitably forewarned and forearmed.
I consider myself warned!
What do you mean here? Without checking, I assumed that the x in `x { ... }` had to be a variable. But this is wrong! See 3.15.3 of the Haskell 2010 report (https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-490003.1...). So I think it's already generalized.
Good news. This should simplify the implementation.
Many thanks for taking this on! Richard

The patch now validates at D1152.
https://phabricator.haskell.org/D1152
On Tue, Aug 11, 2015 at 11:26 PM, Matthew Pickering
Thank you for your comments Richard.
I'm assuming `pattern Foo{bar, baz} = (bar, baz)` from the wiki page, without any further pattern type signature. This example then looks straightforward to me -- I feel I'm missing the subtlety. `foo` would get the type `(a,b) -> (b,b)` and would be roughly equivalent to `foo a@(bar, baz) = case a of (_, baz2) -> (baz, baz2)`. The case statement and baz2 is necessary just to provide a predictable desugaring of record updates; handwritten code should clearly be more succinct.
This is how I imagined it to work.
This would desugar to `foo x = case x of Just _ -> Just 5`. I'm not sure about pattern exhaustiveness warnings, but I would expect such a record update to be partial. The partiality of record updates has been surprising in the past, but I don't think adding pattern synonyms to the mix should change that.
Yes, I agree.
I would like to keep record updates for the same reasons you appear to. I will warn that they are quite hard to work with, though! About 220 lines of dense code (including comments) are necessary to type-check regular old record updates. This isn't to scare you off, but to have you suitably forewarned and forearmed.
I consider myself warned!
What do you mean here? Without checking, I assumed that the x in `x { ... }` had to be a variable. But this is wrong! See 3.15.3 of the Haskell 2010 report (https://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-490003.1...). So I think it's already generalized.
Good news. This should simplify the implementation.
Many thanks for taking this on! Richard

Record field updates via patsyns looks very weird to me (and, as just a
user, it would be unexpected). Can't we do just matchers and builders for
now, and add field updaters as a second step, if there's concensus that
it's a Good Idea?
Bye,
Gergo
On 11 Aug 2015 07:11, "Matthew Pickering"
I was looking at implementing #8582 but before I got too far I thought it best to clear up a few design points.
A summary can be found below and a more fleshed out version with some examples can be found on the wiki page[1].
My main question is about how best to deal with record updates. Say that Foo is a record pattern synonym then how would we expect the following program to behave?
``` foo a@Foo{..} = a {bar = baz} ```
Then say that `pattern Foo{bar} = Just bar`, how should the following two programs behave? Is this partiality any different to that caused by ordinary use of pattern synonyms? (At least partiality in patterns is warned but how comprehensive is the coverage?)
``` foo :: Maybe a -> Maybe Int foo x = x {bar = 5}
-- error as `bar` unique determines that we need Foo bar = Nothing {bar = 5} ```
Abandoning record updates seems to make record syntax for pattern synonyms far less useful and confusing to users. Is this design how others have imagined it? I have cced Gergő who originally implemented the extension and created #8582.
Matt
----
Unidirectional patterns * Provide the same ability to match as normal records (RecordWildcards etc) * Provide selector functions
Bidirectional patterns * Provide the constructor which can be used as normal record constructors
Record Updates - unclear * Generalise update syntax to arbitrary expressions?
[1]: https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#RecordPatternSynonyms _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

I think debating the overall idea before implementing is a great idea. Here's my reason why I like these: it allows a library designer to change an internal representation of a previously-concrete datatype while providing backward compatibility. Since the datatype had been exporting its constructors, clients might have used record update. Now the author can change the datatype and add pattern synonyms to keep the interface constant. Without the feature proposed here, such a change would be impossible.
Furthermore, record update syntax is awfully convenient, and may be attractive to new libraries with abstract types. I haven't tried to do it, but I imagine you could do some cool lens-like constructs with proper (ab)use of this feature.
Richard
On Aug 11, 2015, at 9:05 AM, Dr. ÉRDI Gergő
Record field updates via patsyns looks very weird to me (and, as just a user, it would be unexpected). Can't we do just matchers and builders for now, and add field updaters as a second step, if there's concensus that it's a Good Idea?
Bye, Gergo
On 11 Aug 2015 07:11, "Matthew Pickering"
wrote: I was looking at implementing #8582 but before I got too far I thought it best to clear up a few design points. A summary can be found below and a more fleshed out version with some examples can be found on the wiki page[1].
My main question is about how best to deal with record updates. Say that Foo is a record pattern synonym then how would we expect the following program to behave?
``` foo a@Foo{..} = a {bar = baz} ```
Then say that `pattern Foo{bar} = Just bar`, how should the following two programs behave? Is this partiality any different to that caused by ordinary use of pattern synonyms? (At least partiality in patterns is warned but how comprehensive is the coverage?)
``` foo :: Maybe a -> Maybe Int foo x = x {bar = 5}
-- error as `bar` unique determines that we need Foo bar = Nothing {bar = 5} ```
Abandoning record updates seems to make record syntax for pattern synonyms far less useful and confusing to users. Is this design how others have imagined it? I have cced Gergő who originally implemented the extension and created #8582.
Matt
----
Unidirectional patterns * Provide the same ability to match as normal records (RecordWildcards etc) * Provide selector functions
Bidirectional patterns * Provide the constructor which can be used as normal record constructors
Record Updates - unclear * Generalise update syntax to arbitrary expressions?
[1]: https://ghc.haskell.org/trac/ghc/wiki/PatternSynonyms#RecordPatternSynonyms _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs _______________________________________________ ghc-devs mailing list ghc-devs@haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/ghc-devs

On Tue, Aug 11, 2015 at 9:11 AM, Richard Eisenberg
I haven't tried to do it, but I imagine you could do some cool lens-like constructs with proper (ab)use of this feature.
Seems likely given that generalizing record update was the original impetus for lenses. :) -- brandon s allbery kf8nh sine nomine associates allbery.b@gmail.com ballbery@sinenomine.net unix, openafs, kerberos, infrastructure, xmonad http://sinenomine.net
participants (4)
-
Brandon Allbery
-
Dr. ÉRDI Gergő
-
Matthew Pickering
-
Richard Eisenberg