[Haskell-beginners] Lifting over record syntax

Anthony Clayden anthony_clayden at clear.net.nz
Tue Oct 30 03:20:11 UTC 2018


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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/beginners/attachments/20181030/5da1edb8/attachment.html>


More information about the Beginners mailing list