Anonymous record modifier syntax proposal

213 views
Skip to first unread message

Vincent Goossens

unread,
Nov 25, 2013, 8:01:01 PM11/25/13
to elm-d...@googlegroups.com
Record syntax in Elm is great but I feel like it misses some building blocks to make full use of extensible records.

A pattern I regularly encounter in my testing code is
map (\x -> { x | field <- f x.field }) xs
which is how you modify a field anonymously. Compared to just accessing the fields
map .field xs
that's a lot of boilerplate and it composes a lot worse. You're basically using a lambda solely to bring a name in scope; that's against the paradigm. I've got two propositions to fix this and they could be used alongside each other in different situations.

One is a function constructor that could look like
{\ x | field <- f x.field } or something like that
meaning the same thing as the lambda above. This matches the original syntax and the functionality for adding and removing record fields, and updating multiple fields at a time is preserved.

The other one is focused on updating a single field. I took the operator from Kmett's lenses for familiarity, and because it's illegal Elm right now:
%field f
which means the same as the lambda above.
The whole mapping could now be written as
map (%field f) xs
This matches field access function syntax and thus can be easily used together with prelude functions, e.g., if you want to modify multiple fields:
map (%a f1 . %b f2 . %c f3) xs
It also allows field names to be used as a parameter for higher order functions, considering
%field === (\f x -> { x | field <- f x.field }) : (a -> a) -> {b | field : a} -> {b | field : a}
which opens up a bunch of possibilities. The type is more restrictive than it should be, but I think it makes more sense this way; fields shouldn't just change type.

The latter would be the most valuable addition, I suppose, as it's the most different. It cuts down on a lot of boilerplate for regular tasks and I can stop worrying about whether or not I should write withField functions. I've used this style a few times in a recent project and the type checker doesn't complain; this could be implemented as a preprocessing macro.

Vincent Goossens

unread,
Nov 26, 2013, 5:36:41 PM11/26/13
to elm-d...@googlegroups.com
I've studied the compiler and it seems that this can be implemented without touching anything other than the parser. Should I do a pull request?

I'm interested in feedback on the syntax, and examples for higher order functions (using the %field syntax) that could be helpful in Elm to promote the idea. 

One question I've come up with for the %field syntax: is it a problem that %field and .field are unrelated? They can't be derived from each other. This might be awkward in some cases.

Dobes Vandermeer

unread,
Nov 26, 2013, 5:58:36 PM11/26/13
to elm-d...@googlegroups.com
You might want to add it to your own fork of Elm and then show a working example application that uses it.  Then people can weigh in on how it looks and feels.

I think Evan is reluctant to add to the language without doing some sort of trial run like this first.

Personally I'm not a fan of the % but I don't have anything better coming to mind.  It does look handy, but whether it would get enough use to have its own syntax I'm not sure.

I wonder if this is something that could be implemented as a function instead of a syntax extension.   Maybe a version of map like "fieldMap".

If you could somehow use the same ".foo" value to set a field we'd be in business - basically a kind of "first-class field" feature.  Maybe some built-in function that takes a field getter ".foo" and turns it into a setter?




--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Vincent Goossens

unread,
Nov 26, 2013, 7:36:06 PM11/26/13
to elm-d...@googlegroups.com

On Tuesday, November 26, 2013 11:58:36 PM UTC+1, Dobes Vandermeer wrote:
You might want to add it to your own fork of Elm and then show a working example application that uses it.  Then people can weigh in on how it looks and feels.

Is it common for people to try out forks here? If not I suppose a macro might be more worth it since it would require less work for people to experiment with. 
 
Personally I'm not a fan of the % but I don't have anything better coming to mind.  It does look handy, but whether it would get enough use to have its own syntax I'm not sure.

Secretly I'm experimenting with some kind of State monad variant specifically for Automatons and it all works rather well, but check this out:
type RNG a = {a | seed : Int}
nextRng x = mod (13 * x + 654) 701

getRnd : State (RNG a) Int
getRnd = modify (\a -> { a | seed <- nextRng a.seed}) >> gets .seed
The difference between working with modify and gets is huge, just because of some sugar. 
 
I wonder if this is something that could be implemented as a function instead of a syntax extension.   Maybe a version of map like "fieldMap".

If you could somehow use the same ".foo" value to set a field we'd be in business - basically a kind of "first-class field" feature.  Maybe some built-in function that takes a field getter ".foo" and turns it into a setter? 

Those aren't possible right now. There's only shorthand notation for getters and for something like that we'd also need setter syntax. Those still aren't derivable from each other, but you can get the modifier if you have both. I'd like to note that I've stepped away from making setField functions in my code, because I use them a lot less and they are a bit harder to work with in regards to getting help from the type checker (they can set or add a field depending on the value you put in, and it causes problems when a field changes type).
BTW, what you're proposing is akin to lenses (introductory tutorial) which were introduced to fix Haskell's records. Those require a bunch of power, though. I don't think Elm needs that.

Evan Czaplicki

unread,
Nov 26, 2013, 9:06:54 PM11/26/13
to elm-d...@googlegroups.com
This is cool, but I definitely do not want to add this in the next release. Big changes in 0.11 will mostly be around working nicely with elm-get, and Dobes is right that I really like to see the code before acting on this kind of thing. I think doing a preprocessor might be the best approach for now :)

I dislike the % syntax pretty strongly. I also think that composing a bunch of modifications is going to be way slower if implemented naively (i.e. I think you need pretty clever optimizations to make this not silly to compute). It may be worthwhile to think about adding an operator into the existing record syntax specifically for modify (in addition to <- and =) so that many can happen at once and you have more syntax freedom. That may also be a bad idea.

Overall, this kind of expressiveness is not the primary priority right now. I am planning to address JSON <-> records after elm-get. I think that can be a really awesome part of Elm if I can get it right. The more this applies to JSON <-> records, the sooner it is relevant to my near term plans.

Also, I feel like how functional people use and think about records is changing pretty quickly right now. I don't want to rush to commit to more syntax when I think there are some new patterns and techniques that are getting worked out right now (like in the OO thread recently).


--

Vincent Goossens

unread,
Nov 28, 2013, 1:46:25 PM11/28/13
to elm-d...@googlegroups.com
I've forked it and added the %field syntax. 

Regarding that thread on OO. The "records with functions in them" style seems to be the most talked about. That pattern actually works quite well with mutators.
Dobiasd's article on this shows the use of the pattern with a closure over a single value (Int or String) but when you need more, you're going to use a record. It works nice with extensible records and mutators, it saves a lot of boilerplate. That way, you don't have to define the records beforehand, and there's no need for de/reconstructing the object.

Laszlo Pandy

unread,
Nov 29, 2013, 11:57:27 AM11/29/13
to elm-d...@googlegroups.com
What I like about the get function syntax is how obvious it is:
map .name people

Once you understand that an anonymous function can look like .name, you will never forget it's meaning. Your idea for setting is less obvious. So my question is, how what would it be to implement this syntax?:
map (.name<- "Evan") list

Where .name<- is a function, and "Evan" is the first argument to it.

Max Goldstein

unread,
Nov 29, 2013, 2:51:44 PM11/29/13
to elm-d...@googlegroups.com
I really like that concrete syntax, Laszlo, but what if the new value of name depends on the old value of name? One possibility is to use the field name: map (.name<- String.toUpper name) list but as field names can be arbitrary you're shadowing an arbitrary token.

Sean Corfield

unread,
Nov 29, 2013, 3:48:38 PM11/29/13
to elm-d...@googlegroups.com
In Clojure, update-in (and several other functions that "modify" parts
of a data structure) take a function that is applied to the existing
value.

Using Laszlo's suggestion, we'd get something like:

map (.name<- (constantly "Evan")) list

where constantly foo yields a function that when applied to any value
yields the value of foo.

For Max's example, you'd do:

map (.name<- String.toUpper) list

It makes simple setting operations a bit more verbose but solves the
general case in an elegant way.

Sean
> --
> You received this message because you are subscribed to the Google Groups
> "Elm Discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to elm-discuss...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.



--
Sean A Corfield -- (904) 302-SEAN
An Architect's View -- http://corfield.org/
World Singles, LLC. -- http://worldsingles.com/

"Perfection is the enemy of the good."
-- Gustave Flaubert, French realist novelist (1821-1880)

Sean Corfield

unread,
Nov 29, 2013, 3:50:46 PM11/29/13
to elm-d...@googlegroups.com
And just to show the Clojure equivalent to both of those:

(map #(update-in % [:name] (constantly "Evan")) list)
(map #(update-in % [:name] clojure.string/upper-case) list)

The #(... % ...) notation is an anonymous function with % as a
placeholder for the argument. You could write (fn [x] (... x ...))
instead.

Sean

Vincent Goossens

unread,
Nov 29, 2013, 5:19:37 PM11/29/13
to elm-d...@googlegroups.com
The syntax is a problem, yeah. Mind that it needs to be in prefix notation, and it shouldn't break the existing syntax. Be aware that <- looks like "... is smaller than minus..." as a loose term. There shouldn't be things that can be expressions on both side of an arrow.

Not that I don't like how it looks. It's a lot better than that stupid percent sign but I didn't find anything else that wasn't taken yet.

seanco...@gmail.com

unread,
Nov 29, 2013, 10:13:36 PM11/29/13
to elm-d...@googlegroups.com
Vincent, it would help the discussion if you quoted the specific pieces of emails you are responding to…

Sean Corfield -- (904) 302-SEAN

An Architect's View -- http://corfield.org
--

Evan Czaplicki

unread,
Nov 29, 2013, 10:39:57 PM11/29/13
to elm-d...@googlegroups.com
I think the .name<- syntax is clearer, but somewhat scary. I think the general feeling is that the type would be as follows:

.name<- : (a -> b) -> { r | name:a } -> { r | name:b }

Is there precedent for having such intense whitespace sensitivity and symbol usage in other languages? I know OCaml and SML have something along the lines of what Laszlo mentioned (shown here). Those have the following type though:

b -> { r | name:a } -> { r | name:b }

which seems less useful in terms of saving characters. It is a weird syntactic structure in both of those ML's, but I don't think it is quite as weird as what we are talking about.

In any case, do we have more examples of this kind of syntax?

Max Goldstein

unread,
Nov 29, 2013, 11:13:28 PM11/29/13
to elm-d...@googlegroups.com
Sean: Oh of course, it's just a higher order function. The lack of the constantly had me thinking it was an expression. Incidentally, the Haskell people call that const, and it's the function I will push the strongest to be added to Basics, when we get a chance. In the mean time just write: const a _ = a

Evan: It's not applicable to statically typed languages, but D3 and probably many other JS libraries use the same function for getting and setting, based on the number of arguments. You could try .nameGets or some other lingual rather than symbol difference.

Vincent Goossens

unread,
Nov 29, 2013, 11:55:07 PM11/29/13
to elm-d...@googlegroups.com
My fork now contains the syntax %field (which does the same as the discussed .field<-, just to be clear) and the {\ x | [regular record syntax, with x bound] } syntax. 

Example usage is shown in tests/good/Mutators.elm. It works as expected. The goal of not using anonymous functions just to bind a variable has been reached in both cases. I'm not satisfied about the syntax, though it does look neater in the code than you'd think. Be aware for now that (%) can't be a function anymore.

I implemented it by altering the parser alone. I've studied the compiler for a bit and I don't think I've broken optimization rules. Everything should just work and all your old Elm files will still work fine.

PS: Sorry Sean, didn't realize people were reading these threads in their email client.

Dobes Vandermeer

unread,
Dec 1, 2013, 2:21:22 AM12/1/13
to elm-d...@googlegroups.com
I think Max is into something here.

Maybe something like:

.name = (\_1 -> _1.name)
.name.setter = (\_1 _2 -> { _1 | name <- _2 })
.name.mapper = (\_1 _2 -> { _1 | name <- _2 _1.name })

Since we're already being tricky with the dot ... ?

Still a bit tricky but if later records became callable via a default field (ala python __apply__) then .name could return a record and all the dots would make sense.



On Fri, Nov 29, 2013 at 8:13 PM, Max Goldstein <maxgol...@gmail.com> wrote:
Sean: Oh of course, it's just a higher order function. The lack of the constantly had me thinking it was an expression. Incidentally, the Haskell people call that const, and it's the function I will push the strongest to be added to Basics, when we get a chance. In the mean time just write: const a _ = a

Evan: It's not applicable to statically typed languages, but D3 and probably many other JS libraries use the same function for getting and setting, based on the number of arguments. You could try .nameGets or some other lingual rather than symbol difference.

--

Jeff Smits

unread,
Dec 1, 2013, 8:33:23 AM12/1/13
to elm-discuss
That looks simple enough, but I think it will be though to get the type system to accept records as both data and a function that can be called.
Message has been deleted
Message has been deleted
Message has been deleted

Max Goldstein

unread,
Dec 1, 2013, 1:45:33 PM12/1/13
to elm-d...@googlegroups.com
I don't like Dobes's distinction between the setter and the mapper. You will usually want the old field value (or the entire record!) to determine the new value. If you want to set it to an expression, use const or whatever it winds up being called.

Counterproposal, with decent variable names:

.name == (\rec -> rec.name)
.name<- == (\f rec -> rec.name <- f r)
where r is either rec or rec.name. I think that's the interesting design decision to make.

I'm not sold on the <- suffix, but it does evoke the assignment arrow of the expanded form.

Evan Czaplicki

unread,
Dec 1, 2013, 3:32:54 PM12/1/13
to elm-d...@googlegroups.com
I think Dobes idea is interesting :) I would expect this though:

.name.set 42
.name.map sqrt

This is weird if people name fields set or map though. Jeff, I don't fully understand your concern. I think the idea is that ._.map is a syntactic unit where some field name goes in the blank, so it is distinct from normal field access and the ._.get pattern. Perhaps this is an argument for using something besides a dot there (although everything else seems to look weirder or be syntactically ambiguous)

I don't think I have a favorite at this point. If something like this is going to be added, I'd like there to be a unifying concept or system that points towards a general way that we should extend things. That is why Dobes' idea is appealing to me. I think having a solid semantic reasoning for how we came to the particular syntax is important, and I don't think we are there yet.


--

Dobes Vandermeer

unread,
Dec 1, 2013, 11:49:10 PM12/1/13
to elm-d...@googlegroups.com

Er, so what you're saying in terms I can understand is:

Add a new operator which takes two records; for each field the exists in the left record, apply it as a function to the same-named field in the right record and put the result into the same-named field in a new record.  Fields not mentioned on the left are left unchanged?

That would be a nice solution as well.  It seems reminiscent of the whole idea of a new operator that can take a .foo and turn it into a mapper.  i.e.

.foo rec == rec.foo
fieldMapper .foo f rec == { rec | foo <- f rec.foo}

The definition of fieldSetter would have to be "magic" in that it can accept one of those getter functions and do the right thing.  

So would <<*>>, I don't think Elm has the required metaprogramming capability to do that in Elm.

I think I like these approaches both better than the double-dot thing I suggested before, but they do require some more "builtins" in the language which makes them more difficult to implement.



On Sun, Dec 1, 2013 at 7:25 AM, Vincent Goossens <vncnt...@gmail.com> wrote:
Some really good ideas here, I'll implement some more of them, I think.

I've seen an other approach to this problem in the Vinyl package for extensible records in Haskell. They treat records as a generalization of Applicative (i.e., without pure). When we port the record syntax to Elm it would look like:

type Person = { name : String , age : Int }
bob = Person "Bob" 42
{ name = toUpper } <<*>> bob === { name = "BOB" , age = 42 }

I don't know whether (<<*>>) can be implemented as plain Elm, but it'll suffice. Then we need an infix version (liftR?) to use it for higher order stuff. (or are operator sections under the way?).

Also cool is that you can do stuff like { age = (+) } <<*>> bob <<*>> bob because they're just applicatives. This does fall short when you need to access other fields.

(My comment was deleted, don't know why. I've typed it again. Sorry if it pops up again later on.)

Jeff Smits

unread,
Dec 2, 2013, 2:01:06 PM12/2/13
to elm-discuss
Jeff, I don't fully understand your concern. I think the idea is that ._.map is a syntactic unit where some field name goes in the blank, so it is distinct from normal field access and the ._.get pattern. Perhaps this is an argument for using something besides a dot there (although everything else seems to look weirder or be syntactically ambiguous)
 
I was actually talking about Dobes idea of a record that can be called as a function which implicitly calls a function in a magic __apply__ field of the record. Although thinking it over, I guess even that could be syntactically distinguished... Still, applicative records sound a bit too flexible to me.

Anyway, this newest suggestion with the (<<*>>) operator doesn't sound like a good idea to me. It would seem to imply that what's going on is a very normal operation between two records, whereas I don't think you can likely use two arbitrary records. In other words, I don't think you can define the type of (<<*>>), so to get it to work it should be special syntax but that it not obvious because it looks like a normal operator.

I think of the things discussed I like Dobes idea of the double-dot notation best, with Evans names set and map. But still, it's not ideal. I'm not convinced this is "good enough", though I understand a record modifier syntax would be useful.

Vincent Goossens

unread,
Dec 2, 2013, 2:55:16 PM12/2/13
to elm-d...@googlegroups.com
Yeah, i didn't think too long about the type of (<<*>>). It can't be expressed in Elm's type system. It even requires some controversial extensions in Haskell.

@Dobes: These builtins aren't that harder to implement. They're just slower to parse. Since the pattern ".field.xxx" only happens with set and map, it can be put in the parser. For a prototype that's good enough. Maybe current optimizer can already deal with it.

Some questions, do these make sense?
a.field.map : (a -> b) -> {r | field : b}
a.field.set : b -> {r | field : b}

Will Elm support this?
.field.subfield : {b | field : {c | subfield : a}} -> a
That would invalidate my easy parser approach, but it's still doable, and can be extended naturally with set and map.

(Btw, while checking the type of that .field.subfield I discovered a bug in the type printer.)

Dobes Vandermeer

unread,
Dec 2, 2013, 4:46:04 PM12/2/13
to elm-d...@googlegroups.com
@Vincent:

I guess if done at parse time, the operation would always have to take a literal record on the left.  I was thinking it would work even if the record on the left was an expression that resulted in a record, as long as the record had a bunch of functions in it.

Good point about the sub-field accessor although I guess one can use composition to do that for a few extra keypresses.  Nevertheless, someone who was familiar with the .field syntax might more logically expect .field.map to be (\r -> r.field.map) if they weren't aware of this other trick yet.

@Jeff:

I agree with your suggestion that we don't seem to be hitting on the right thing yet, we should keep exploring.






Martin DeMello

unread,
Dec 2, 2013, 8:44:27 PM12/2/13
to elm-d...@googlegroups.com
Another option: parse a {{ }} block as a field updater.

{{ x | name <- "Evan" }}  # set to a constant
{{ x | name <- x.firstName + x.Lastname }} # set a field from the entire record
{{ x | name |> upcase }} # pipe a field through a function
{{ .name |> upcase }} # make the record optional if we don't need it
{{ .name |> upcase |> truncate 40 }} # just the normal pipe operator on the rhs
{{ .name |> case ; .score <- 0 }} # multiple field updates

Haven't worked it all the way through, but it feels like it should be doable purely as a series of desugarings.

martin


Vincent Goossens

unread,
Dec 2, 2013, 10:12:39 PM12/2/13
to elm-d...@googlegroups.com
That actually corresponds to the two of my suggestions in the first post :) You've seem to have found an interesting way to bring them together. 
Oh, and it'll be easy to write. I'm doing it tomorrow so I can experiment a bit (after writing .map and .set).

The first three work as {\ x | bla} in my fork, I'll change it to yours.
The others are a bit like but less flexible than %field, because the function must be bound inside the brackets. How about {{ field }} in stead of yours? We can have both of them, though. I can certainly see that yours is prettier depending on the situations. 

We could also go crazy and do a {{ x, y |  sum <- x.a+ y.b }} which accepts two records and creates a new record {sum:Int}. This differs in that no unused fields are preserved. Maybe if there's a way to indicate a base record, but I can only come up with something ugly like {{ x <- y, z |  sum <- x.a+ y.b + z.c}} (which gives a {r|sum:Int, a:Int} of course).

I'll also look in to .field.subfield accessor syntax. I think that'll be worth it even if modifiers won't make it.

Vincent Goossens

unread,
Dec 2, 2013, 10:32:36 PM12/2/13
to elm-d...@googlegroups.com
@Vincent:
I guess if done at parse time, the operation would always have to take a literal record on the left.  I was thinking it would work even if the record on the left was an expression that resulted in a record, as long as the record had a bunch of functions in it.

What do you mean? Back to %field in stead of .field.map? $field for a setter maybe? It's ugly, but it's fast to parse...

Side remark, something I've been thinking about, .map can't be represented as a field with mapper function stored in it, not even implicitly. After all, field could be of type Float just as well, and that where .map would be applied to. That doesn't play well with the type system. So it has to be pure syntactic sugar if it's done like that.
What does type check is giving every record an implicit field map with a mapper for every field in it. Then it would be .map.field. The bad new is that way we cant get to  .map.field.subfield anymore, unless with pure syntactic sugar, again.
Don't know where I'd like my .map to be. If it's in front and we need to use magic, it'll parse faster.

Evan Czaplicki

unread,
Dec 2, 2013, 10:46:54 PM12/2/13
to elm-d...@googlegroups.com
Parsing this should be fine. A fast parser for a programming language usually looks ahead by only one token (something like an LL(1) parser). In the case of .field.map, you only need a lookahead of one (which is optimal). When you see a dot, there are many paths you can take. If you see a character, you know to take the field access path. Then you just accept safe variable characters. Next lookahead is for space or dot. You branch again on that. So probably don't worry about parsing speed right now :)


--

Dobes Vandermeer

unread,
Dec 2, 2013, 11:01:31 PM12/2/13
to elm-d...@googlegroups.com
Hi Vincent,

I was referring to the <<*>> operator - to be implemented in the parser you would need a literal record on the lhs.

Cheers,

Dobes



--

Max Goldstein

unread,
Dec 2, 2013, 11:14:06 PM12/2/13
to elm-d...@googlegroups.com
I like Martin's {{ syntax, provided I understand correctly that the type of the entire block is a function from records to records (which can be mapped over a list). And from Evan's parsing comments, it sounds like detecting {{ is efficient. I don't follow all the details with |> but it's worth exploring.

Martin DeMello

unread,
Dec 2, 2013, 11:30:18 PM12/2/13
to elm-d...@googlegroups.com
On Mon, Dec 2, 2013 at 8:14 PM, Max Goldstein <maxgol...@gmail.com> wrote:
I like Martin's {{ syntax, provided I understand correctly that the type of the entire block is a function from records to records (which can be mapped over a list).

yes, precisely
 
And from Evan's parsing comments, it sounds like detecting {{ is efficient. I don't follow all the details with |> but it's worth exploring.

what i intended with |> is that within a {{}} block, field |> function
be desugared to field <- field |> function

the nice thing is that if you want a longer pipeline where the function contains further |>s, that is just the normal elm pipe operator, and everything works as expected.

martin

Martin DeMello

unread,
Dec 2, 2013, 11:48:24 PM12/2/13
to elm-d...@googlegroups.com
Yes, my proposal was directly inspired by your first post :)

Not sure what you mean about the function needing to be bound within the 
brackets - it does need to be, but it would anyway need to be bound within the enclosing scope whatever syntax we adopted. Instead of

map (%field f) xs

we'd have

map {{.field |> f}} xs

but in either case f would need to be bound in the scope map is in. The point of the .field is to indicate that we are not referring to the whole record anywhere, so it can be desugared into

map {{ gensym | field |> f }} xs

which in turn becomes

map {{ gensym | field <- field |> f }} xs

which is currently valid elm syntax. (The parser will have to carry a context around that says "strip a leading dot off field names within the {{}}", but if we did not go that route, the first | would be ambiguous until we saw whether it was an |> or the record name delimiter)

martin


Vincent Goossens

unread,
Dec 3, 2013, 9:52:33 AM12/3/13
to elm-d...@googlegroups.com
%field has type (a -> b) -> {r | field : a} -> {r | field : b} what means that you can provide the mapping function that it uses anytime you want. So you can have this
inc a = a + 1 
mapIncOverField field xs = map (field inc) xs
mapIncOverAandB xs = (mapIncOverField %a) . (mapIncOverField %b)

In stead of passing %a, you'd have to pass (\f -> {{.a |> f}}). The lambda is needed to bind the function to a variable so it's in the scope of the {{}}.
That said, while it's less flexible, it'll be enough in the most common cases, and it's prettier.

Vincent Goossens

unread,
Dec 6, 2013, 11:23:24 PM12/6/13
to elm-d...@googlegroups.com
New push at my test branch https://github.com/VulumeCode/Elm
  • Suffix .set and .map -- I've added it just in case. I want to look in to .field.subfield accessor syntax, but that might not be compatible.
  • Moustache modifier: {{ x | bla }} = (\a ->{a| bla})
  • Moustache modifier: {{a, b, c | bla}} = (\a b c ->{a| bla}) -- a cool pattern would be {{x, a, b | sum = a.field1 + b.field2}} {} <~ aSig ~ bSig. The empty new record {} is fed into the modifier and bound to x, to build a new record of type {sum:number}. We can then lift this and use it to combine different types of signals. 
  • Moustache modifier maker: {{field}} =  (\f a -> {a| field <- f a.field} -- not so sure about this one. It's %field, but I feel it doesn't jive with the other moustaches. This is better:
  • Up next: Moustache modifier without explicit bindings: {{ field1 |> f, field2 |> g |> h}} = (\a -> {a| field1 <- f a.field1, field2 <- h (g a.field2)})
Martin, I really like the idea. The parser is giving me some headaches but I'm working on it!

Evan, did you know you can make values of type {sameName: a, sameName: b} ? That's really weird. It happens when you use regular setter syntax {a | sameName = a.sameName}. I know it's the wrong use case and you should use <-, so, is this a feature or a bug? 

Jeff Smits

unread,
Dec 7, 2013, 8:32:53 AM12/7/13
to elm-discuss
Evan, did you know you can make values of type {sameName: a, sameName: b} ? That's really weird. It happens when you use regular setter syntax {a | sameName = a.sameName}. I know it's the wrong use case and you should use <-, so, is this a feature or a bug? 

I can answer that one. It's a feature. Not that I've ever heard anyone using it, but it's a feature. I looked up the original announcement for you. That links to a research paper of the system that Evan implemented. 
The idea of adding more that one field with the same label is you can overlay the old one without forgetting about it and you can later remove the overlay again to get the old value back. I don't remember if there is actually syntax in Elm for removing an overlay field, but then again I've never actually found a situation where this overlaying may be handy. (sorry for the opinionated explanation)

Martin DeMello

unread,
Dec 9, 2013, 2:40:33 PM12/9/13
to elm-d...@googlegroups.com
On Fri, Dec 6, 2013 at 8:23 PM, Vincent Goossens <vncnt...@gmail.com> wrote:
  • Moustache modifier maker: {{field}} =  (\f a -> {a| field <- f a.field} -- not so sure about this one. It's %field, but I feel it doesn't jive with the other moustaches. This is better:
  • Up next: Moustache modifier without explicit bindings: {{ field1 |> f, field2 |> g |> h}} = (\a -> {a| field1 <- f a.field1, field2 <- h (g a.field2)})
The more I think about this, the more I feel that {{field}} is not a good idea, not just because it doesn't fit in with the other moustaches, but because the postfix function application feels very out of place. I had the same issue with the original %field proposal - (%field f) really seems like it wants an operator in the middie.

martin 

Christian Stork

unread,
Dec 11, 2013, 1:10:56 AM12/11/13
to elm-d...@googlegroups.com
Hi,

reading this thread I had an idea inspired by these syntactic constructs:

{ rec | fld <- f rec.fld }
{ rec | fld <- f rec.fld1 rec.fld2 ... }

f <| rec

f <~ a ~ b ~ ...

Some examples of my proposed syntax and what it corresponds to:

f <.fld rec                    { rec | fld <- f rec.fld }
f <.fld1|fld2|... rec          { rec | fld1 <- f rec.fld1 rec.fld2 ... }
f .fld0<.fld1|fld2|... rec     { rec | fld0 <- f rec.fld1 rec.fld2 ... }

Now the idea is to treat <.fld and .fld0<.fld1|fld2|... just like a modified function application <|, i.e., like a single infix operator -- no spaces allowed (except after the pipe maybe).  I like to think of it as specifying a way to "lift a function application into a record".

Once operator sections are available in Elm this allows for some very readable code (to my eyes, anyway):

map (f <.fld) recs
map (f .fld0<.fld1|.fld2) recs

Furthermore, this scheme seems to be very amenable to all sorts of extensions.

Operating on subfields:

f <.fld.sub rec

Using .. to pass the parent record (reminiscent of Posix' .. parent directory) as a function argument:

f .fld<.. rec                  { rec | fld <- f rec }

(Maybe f <..fld rec would be even shorter.)

Removing fields:

f .fld0<.-fld1 rec             { rec - fld1 | fld0 <- f rec.fld1 }

Adding fields:

f .fld0=.fld1                  { rec | fld0 = f rec.fld1 }

which gives us the following correspondence

f <.x   <=>   f .x=.-x

and provides a nice way to rename fields:

id .fld0=.-fld1

Updating or adding a field with a constant:

.fld< "constant"               { rec | fld <- "constant" }
.fld= "constant"               { rec | fld = "constant" }

The latter would not be parsed as infix operators but as functions, of course.

What do you guys think?

-Chris

Vincent Goossens

unread,
Dec 11, 2013, 11:23:03 PM12/11/13
to elm-d...@googlegroups.com
That's great! I'm just afraid of having ambiguous operators, though. I mean, I'm having trouble with the dot syntax sometimes already... It sneaks up on you when you don't expect it. Maybe that's just because the errors still need some work. :)

Anyways, new push! 

I've added moustache modifiers without explicit bindings {{ .field1 <- f , .field2 <- g . h }} = (\ a -> { a | field1 <- f a.field1 , field2 <- g (h a.field2) })
Doing this with |> doesn't seem to work. It's also more natural to use dots when working with higher order functions. I think getting |> to work would be inefficient at runtime. The value is in the innermost part of the expression.
I've used <- to reflect regular record syntax. I might as well add other regular record syntax stuff here, too. I'll see how that works out, maybe I can use Christian's ideas here.

Also added a moustache lambda {{\ a b c | bla }} = ((\x a b c -> { x | bla }) {})
I realized that  {{x a b | sum = a.field1 + b.field2, prod = a.field1 * b.field2}} {} <~ aSig ~ bSig  was a pattern I actually would use more than one the one that alters an existing record. I added it and I think it looks nice and straightforward. Also, note that to reflect lambda syntax, commas aren't used anymore when naming the terms, and I've allowed underscores to name terms you're not going to use. This also counts for the regular {{ _ a | whatever }}.

Christian Stork

unread,
Dec 12, 2013, 3:26:52 AM12/12/13
to elm-d...@googlegroups.com
On Thu, Dec 12, 2013 at 5:23 AM, Vincent Goossens <vncnt...@gmail.com> wrote:
That's great! I'm just afraid of having ambiguous operators, though. I mean, I'm having trouble with the dot syntax sometimes already... It sneaks up on you when you don't expect it. Maybe that's just because the errors still need some work. :)

I understand your concern.  I think it might boil down to choosing reasonable rules for significant whitespace.  Elm is already doing the reasonable thing with subtraction vs negative numbers, and function composition vs record field access.

Also added a moustache lambda {{\ a b c | bla }} = ((\x a b c -> { x | bla }) {})
I realized that  {{x a b | sum = a.field1 + b.field2, prod = a.field1 * b.field2}} {} <~ aSig ~ bSig  was a pattern I actually would use more than one the one that alters an existing record. I added it and I think it looks nice and straightforward.

Correct me if I'm wrong but it seems to me that the latter pattern is served quite well by what we currently have:

(\a b -> {sum = a.field1 + b.field2, prod = a.field1 * b.field2})
<~ aSig ~ bSig

-Chris
Reply all
Reply to author
Forward
0 new messages