Stream: ideas

Topic: Record shorthand as \.


view this post on Zulip Nikita Mounier (Apr 12 2022 at 16:05):

One piece of syntax that confused me a little when reading through the tutorial is the record shorthand syntax. Record shorthand syntax is where when you define a function that takes a record and returns a field, you can just write.fieldname:

returnFoo = .foo
returnFoo
# { foo : a }* -> a

returnFoo { foo: "hi!", bar: "blah" }
# hi!

It can also be used inline:

capitalizeField = \record, toField ->
    record
        |> toField
        |> Str.capitalize

baz = { foo: "hi!", bar: "blah" }

baz
    |> capitalizeField .foo

However, to me, upon first meeting this shorthand syntax it wasn't clear to me that .foo should be a function, since to my knowledge there was only one way to create a function in Roc: with a backslash (this was mentioned in "A taste of Roc" I think).

I come from Swift, so I'm biased here. Swift has a very similar syntax for making "property access" into a function, called keypath notation, which looks like: \.fieldname. I know it's just a backslash, but in Roc terms it makes it instantly recognizable as a function. It also creates a nicer visual seperation between the function this is being passed into and itself:

baz
    |> capitalizeField \.foo

Ergonomically and semantically this feels much more natural to me. What do you think?

P.S: I know there's actually a third way to create a function in Roc, using tags with payloads. Could a case be made that those should require a backslash too? I'm not sure, and I feel like that's probably out of scope.

view this post on Zulip Brendan Hansknecht (Apr 12 2022 at 16:09):

Sounds pretty reasonable, though it feels like you started writing a lambda but didn't finish: \.foo feels like it should be followed with -> #do something with foo.

view this post on Zulip Brendan Hansknecht (Apr 12 2022 at 16:10):

But that is just my initial reading bias. I don't really think .foo alone is any better in terms of being a reasonable syntax.

view this post on Zulip Nikita Mounier (Apr 12 2022 at 16:10):

Interesting – to me the dot makes it clear that this is a function only interested in accessing a record's field, and that's it. I guess it could be just me being used to Swift keypaths.

view this post on Zulip Nikita Mounier (Apr 12 2022 at 16:34):

What does everyone else think? Would this be a net positive, or would it just add visual clutter?

view this post on Zulip Richard Feldman (Apr 12 2022 at 18:58):

PureScript uses the syntax _.foo for this

view this post on Zulip Richard Feldman (Apr 12 2022 at 18:59):

and so does Scala

view this post on Zulip Richard Feldman (Apr 12 2022 at 19:00):

at least Scala supports (and I think PureScripts supports it too) using _ for arbitrary partial application

view this post on Zulip Richard Feldman (Apr 12 2022 at 19:00):

e.g. if Roc had that, you could do List.map (Str.concat _ "!!!") strings

view this post on Zulip Richard Feldman (Apr 12 2022 at 19:01):

and _.foo would be consistent with that

view this post on Zulip Richard Feldman (Apr 12 2022 at 19:01):

I'm not sure whether that "underscore for partial application" feature would be a net positive or a net negative for Roc, but if we someday did do that, then _.foo would be consistent with that

view this post on Zulip Brian Carroll (Apr 12 2022 at 20:39):

I have the same reaction as @Brendan Hansknecht , the \.foo looks unfinished to me because it's missing an arrow. Or the body of the function is where the argument should be, or something!
@Nikita Mounier it's interesting, I have a different mental model of this. You describe it as a "shorthand syntax" whereas I think of it as a "special function name", not a syntax. It's a function that is automatically made available whenever you make a record. So maybe there is in fact one "other way to create a function"!

view this post on Zulip Jared Cone (Apr 12 2022 at 20:56):

I like the idea of being able to use some character or keyword for partial application, whether it's _ or something else

view this post on Zulip Zeljko Nesic (Apr 12 2022 at 21:02):

One thing confuses me now:

Foo : { baz : Str }

Oof :  { baz : Int }

To which field .baz refers to?

view this post on Zulip Kevin Gillette (Apr 12 2022 at 21:24):

i wonder if then \.{a, b} would desugar into \{a, b} -> {a, b}, and more generally whether some forms of destructing would be usable via shorthand.

view this post on Zulip Nikita Mounier (Apr 12 2022 at 21:30):

To which field .baz refers to?

Either one!

.baz
# Prints: { baz: a }* -> a

This means that it takes any record which has at least a field called baz of any type, and it'll return a value of that type – namely the value stored in the baz field. So if you apple .baz to Foo, it'll return the string, and if you apply .baz to Oof, it'll return the int.

This is why the model of .baz simply being a function that takes any record that has a certain field and spits back out that field really helps me – and why making it look syntactically closer to function declarations seems like the right step.

view this post on Zulip Nikita Mounier (Apr 12 2022 at 21:31):

Kevin Gillette said:

i wonder if then \.{a, b} would desugar into \{a, b} -> {a, b}, and more generally whether some forms of destructing would be usable via shorthand.

Yeah, that's a really interesting idea!

view this post on Zulip Nikita Mounier (Apr 14 2022 at 12:40):

If issue #2695 – Anonymous record update syntax gets implemented I actually think it could pair nicely with backslash syntax for consistency – \&foo desugars to \record, val -> { record & foo: val }

view this post on Zulip Kevin Gillette (Apr 14 2022 at 13:39):

Nikita Mounier said:

If issue #2695 – Anonymous record update syntax gets implemented I actually think it could pair nicely with backslash syntax for consistency – \&foo desugars to \record, val -> { record & foo: val }

I don't understand how that works. Can you give a definition of foo, and then an example of a full expression using \&foo and what the result of evaluating it would be?

Syntactic sugar seems to usually be a simple syntactic transform, but this seems to be contextually dependent on the nature of foo?

view this post on Zulip Ayaz Hafiz (Apr 14 2022 at 13:44):

The original context for this was something like

onClick = \state -> { state & clicks: state.clicks + 1 }

which would become

onClick = \state -> &clicks state (state.clicks + 1)

view this post on Zulip Kevin Gillette (Apr 14 2022 at 15:36):

@Ayaz Hafiz I have little difficulty reading the original (unsugared) form, but a much harder time reading the &clicks syntax. Would that even be nestable (using multiple &x forms together in the same expression)?

I could see \&.clicks + 1 making more sense, but is it so valuable and so often needed that we'd want to introduce a hard-to-google-for \&. syntax that would almost certainly scare off some potential newcomers?


Last updated: Jun 16 2026 at 16:19 UTC