Re: Lifting over record syntax

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Re: Lifting over record syntax

AntC
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
The bad news: Trex is available only in a very old Haskell system,
Hugs. So old that its download has bitrotted, see note here

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.
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


_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners
Reply | Threaded
Open this post in threaded view
|

Re: Lifting over record syntax

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

_______________________________________________
Beginners mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/beginners