nikita-volkov / record

Anonymous records
http://hackage.haskell.org/package/record
MIT License
245 stars 8 forks source link

Section syntax #1

Open nikita-volkov opened 9 years ago

nikita-volkov commented 9 years ago

Problem

How would this work with Applicative notation, like

parsePerson = Person <$> parseName <*> parseAge

Proposal

Unquote an expression of the following form

[r| {name, age} |]

to

\name age -> Record2 age name :: name -> age -> Record2 "age" age "name" name

(Note the reordering of arguments).

Then it will be possible to use it like this:

[r| {name, age} |] <$> parseName <*> parseAge

Extras

This can be extended even further to allow to partially initialize the record. E.g.:

[r| {name, age = 23} |] <$> parseName

Credits for the initial proposal go to a Reddit user htebalaka. Thank you!

htebalaka commented 9 years ago

It occurred to me that if you wanted to support NamedFieldPuns then the syntax is ambiguous in expressions. {a, b = 3} could be the function \a_1 -> {a = a_1, b = 3} or the record {a = a, b = 3} for whatever identifier a is in scope. You could disambiguate with a different quasiquoter I suppose, or a very slightly different syntax if you want something to can be supported unambiguously without a quasiquoter.

nikita-volkov commented 9 years ago

Hmm... Getting trickier. We'll need to research on the subject.

You could disambiguate with a different quasiquoter I suppose.

I'd much rather avoid a quasi-quoter-based logic. We should develop a syntax which we would not have to change in case at some point we implement a preprocessor, or hopefully implement a GHC syntax extension.

htebalaka commented 9 years ago

I kind of assumed that was the goal. I thought it'd be nice to be able to postprocess the generated function (for newtypes mainly, but also for functions like foo :: {a :: Int , b :: Int , c :: Int} -> IO (). If you had something like {<expr>\b, c=3, a} ==> \x y -> <expr> {b=x, c=3, a=y}, which could be used to convert anything expecting a record to a function with positional arguments in any order, without needing a lambda. Then if you had {\b, c=3, a} as shorthand for {id\b, c=3, a} you could disambiguate nicely even in the non-postprocessed case.

Then the syntax without the backslash character would indicate a record pattern/expression, and I think would handle both RecordWildCards and NamedFieldPuns cleanly.

EDIT: I guess full expressions wouldn't work that nicely, since {\x -> x^2\a} ==> \b -> (\x -> x^2) {a = b} would look kind of bad, though just an identifier to the left of the \ would probably handle the important use cases. Granted, even ignoring the postprocessing case, {\a,b} looks pretty nice to me to represent an anonymous function that returns a record.

nikita-volkov commented 9 years ago

Wait a second. I just realised that NamedFieldPuns is about a pattern context. This proposal concerns an expression context. We can have both things interpret the same syntax!

htebalaka commented 9 years ago

They apply to both unfortunately. In an expression let a = 1 in {a} ==> let a = 1 in {a = a}

nikita-volkov commented 9 years ago

Oh..

htebalaka commented 9 years ago

Another option would be something like {\d,\a,c=3,b}, if you wanted to be able to use NamedFieldPuns and lambdas together (to desugar to \x y -> {a=y, b=b, c=3, d=x} for whatever b is in scope.

Though NamedFieldPuns in an expression might cause issues in general; assuming the lenses are in scope and have the same name as the field then barring a special compiler rule for FieldOwner {a,b} could try to parse like {a=lens (Field :: Field "a"), b=lens (Field :: Field "b")} if a and b weren't being shadowed.