GHC support for the new "record" package

Edward Kmett ekmett at gmail.com
Wed Jan 21 12:56:47 UTC 2015


On Wed, Jan 21, 2015 at 4:36 AM, Simon Marlow <marlowsd at gmail.com> wrote:

> On 20/01/2015 23:07, Edward Kmett wrote:
>
>  It is a long trek from "this is plausible" to "hey, let's bet the
>> future of records and bunch of syntax in the language on this".
>>
>
> Absolutely.  On the other hand, this is the first proposal I've seen
> that really hits (for me) a point in the design space that has an
> acceptable power to weight ratio.  Yes there are some corners cut, and
> it remains to be seen whether, after we've decided which corners we want
> to uncut, the design retains the same P2W ratio.
>
> A couple of answers to specific points:
>
>  Re #1
>>
>> The main term and type level bits of syntax that could be coopted
>> that aren't already in use are @ and (~ at the term level) and things
>> like banana brackets (| ... |), while that already has some other,
>> unrelated, connotations for folks, something related like {| ... |}.
>> We use such bananas for our row types in Ermine to good effect.
>>
>> The latter {| ... |} might serve as a solid syntax suggestion for the
>>  anonymous row type syntax.
>>
>
> Why not just use { ... } ?


Mostly because it would conflict with the existing record syntax when used
as a member of a data type.

Using { ... } would break all existing code, while {| ... |} could
peacefully co-exist.

    data Foo = Foo { bar :: Bar }

vs.

    data Foo = Foo {| bar :: Bar |}

You could, I suppose manually distinguish them using ()'s

    data Foo = Foo ({bar :: Bar })

might be something folks could grow to accept.

Another reason that comes to mind is that it causes a further divergence
between the way terms and types behave/look, complicated stuff like Richard
Eisenberg's work on giving us something closer to real dependent types.

 Re #2
>>
>> That leaves the means for how to talk about a lens for a given field
>>  open. Under Adam's proposal that had evolved into making a really
>> complicated instance that we could extract a lens from. This had the
>>  benefit over the current state of the `record` package that we could
>>  support full type changing lenses. Losing type-changing assignment
>> would be a big step back from the previous proposal / the current
>> state of development for folks who just use makeClassy or custom lens
>> production rules with lens to get something similar, though.
>>
>> But the thing we never found was a nice short syntax for talking
>> about the lens you get from a given field (or possibly chain of
>> fields); Gundry's solution was 90% library and almost no syntax. On
>> the other hand Adam was shackled by having to let the accessor be
>> used as a normal function as well as a lens. Nikita's records don't
>> have that problem.
>>
>> Having no syntax at all for extracting the lens from a field
>> accessor, but rather to having it just be the lens, could directly
>> address that concern. This raises some questions about scope, where
>> do these names live? What happens when you have a module A that
>> defines a record with a field, and a module B that does the same for
>> a different record, and a module C that imports both, but, really, we
>> had those before with Adam's proposal, so there is nothing new
>> there.
>>
>
> Right.  So either
> (a) A field name is a bare identifier that is bound to the lens, or
> (b) There is special syntax for the lens of a field name
>
> If (a) there needs to be a declaration of the name in order that we can
> talk about scoping.  That makes (b) a lot more attractive; and if you
> really find the syntax awkward then you can always bind a local variable
> to the lens, or export the names from your library.


Alternately (c) we could play games with ensuring the "name" is shared
despite coming from different fields.

As a half-baked idea, if we pretended all field accessors were names from
some magic internal GHC.Record.Fields module, so that using

data Foo = Foo {| bar :: Bar, baz :: Baz |}

would add an `import GHC.Record.Fields (bar, baz)` to the module. These
would all expand to the same Symbol-based representation, behind the
scenes, so that if two record types were used that used the same names,
they'd just work together, with no scoping issues.

This has the benefit that users could write such import statements by hand
to use fields themselves, no sigils get used up, and the resulting code is
the cleanest it can be.

-Edward
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/ghc-devs/attachments/20150121/3ff54ef3/attachment.html>


More information about the ghc-devs mailing list