Re: [Haskell-beginners] Lifting over record syntax

Hi Jaakko, oh dear you've picked on the most embarrassing part of Haskell.
data Person = Person {name::String, age::Int} deriving Show
Now, I can create maybe-people like in applicative style:
Person <$> Just "John Doe" <*> Nothing
Hmm? That returns Nothing -- is that what you expect? Perhaps Person <$> Just "John Doe" <*> Just undefined But why not Just $ Person "John Doe" undefined
The problem with the [applicative] approach is that it depends on the order of the arguments because it doesn't utilize the record syntax.
You'd think that a leading-edge language like Haskell would have really powerful record abstractions. With ways to avoid the shackles of argument ordering. Then it's sad to admit Haskell's records are feeble. Back in Haskell 98 people were saying "surely we can do better!" Well, essentially nothing's happened. So records are no more than pretty(-ish?) syntax for the ordered arguments style. The label names are not first-class. And field labels for building records only work in very restricted syntactic positions, as you point out:
I would like to get the benefits of the the record syntax but with similar code simplicity as the applicative style has. Do you have ideas is this possible? My non-working pseudo-code would look like:
Person <$> {name=Just "John Doe", age=Nothing}
But this doesn't work, it's syntax error.
You want that bit in braces to be a free-standing expression. That's called "anonymous/extensible records" (search Haskell wiki). "Anonymous" because it's not tied to any particular datatype. It's self-describing: I am a record with two fields labelled `name, age`; and the order of the labelled fields should be arbitrary. "Extensible" because you could start a record with just one labelled field, and extend it with another labelled field. But I'm not sure what the `Maybe` wrapping is doing for you? I'm going to ignore it. I have some good news and some bad news. There is a Haskell you can do n = (name = "John Doe") -- note round parens n_a = (age = undefined | n) -- | for extend the record p = Person n_a For that you need a slightly different data decl data Person = Person (Rec (name :: String, age :: Int)) deriving Show Round parens again; and a mysterious type constructor. But these records are first-class values. (They're a special variety of tuples.) Perhaps you don't need a datatype? type Person = Rec (name :: String, age :: Int) Then you don't need to apply a data constructor. This style of records is 'Trex' -- Typed Records with extensibility https://www.haskell.org/hugs/pages/hugsman/exts.html#sect7.2 The bad news: Trex is available only in a very old Haskell system, Hugs. So old that its download has bitrotted, see note here https://mail.haskell.org/pipermail/hugs-bugs/2018-July/001914.html GHC has had 12 years to come up with something comparable/better. So far, nothing. If you're wanting to get all Applicative and Functorial in GHC, there are heaps of record systems based around Lenses. http://hackage.haskell.org/package/lens-tutorial But that's going way beyond Beginners level; you'll need to call on Template Haskell; and probably add some plumbing of your own. (And under the hood you still have ordered arguments; they're rather better encapsulated.) AntC

On Tue, 30 Oct 2018 at 4:20 PM, Anthony Clayden wrote:
data Person = Person {name::String, age::Int} deriving Show
Now, I can create maybe-people like in applicative style:
Person <$> Just "John Doe" <*> Nothing
... field labels for building records only work in very restricted syntactic positions, ...
To tease out that remark a little: Data constructor `Person` is first-class; we could go person' = Person person' <$> Just "John Doe" <*> Nothing But the following two are nothing like equivalent; so record syntax is not even referentially transparent: Person{ name = "Jane Roe", age = 37 } -- builds a Person record person'{ name = "Jane Roe", age = 37 } In the second, the token preceding the `{ ... }` is not a data constructor (because it starts lower case), so is taken to be a variable/expression denoting a value of type `Person`; and this is datatype update syntax. Why of type `Person`? Because field labels `name` and `age` come from there, and under H98 records, they can be associated only with a single type. Then `person'` is the wrong type, and you'll get a type error. Although both look like a name (of function type) adjacent to a term { in braces}, neither is function application. AntC
participants (1)
-
Anthony Clayden