Stream: ideas

Topic: Shorthands for nested records


view this post on Zulip Aurélien Geron (Oct 07 2024 at 02:17):

The record shorthand is quite handy: {a: 42} |> .a is much nicer than {a: 42} |> \rec -> rec.a.
To access fields in nested records, we can chain pipes and record shorthands:

{a: {b: {c: 42}}} |> .a |> .b |> .c

Perhaps this has been discussed before, but it would be nice to be able to write this instead:

{a: {b: {c: 42}}} |> .a.b.c

view this post on Zulip Aurélien Geron (Oct 07 2024 at 02:21):

In particular, it would make List.map a bit nicer when dealing with nested records. For example:

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map \rec -> rec.a |> .b

Would become:

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map .a.b

view this post on Zulip Brendan Hansknecht (Oct 07 2024 at 02:52):

just to be fair, I think today, users would write:

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map \rec -> rec.a.b

view this post on Zulip Brendan Hansknecht (Oct 07 2024 at 02:52):

But yeah, I think nested record update and record selection would be great

view this post on Zulip Aurélien Geron (Oct 07 2024 at 02:53):

Oh true, I hadn't thought of that. Just a nice-to-have.

view this post on Zulip Agus Zubiaga (Oct 07 2024 at 17:05):

Yeah, unless we want to discourage nesting records, I think this should just work

view this post on Zulip Richard Feldman (Oct 07 2024 at 18:11):

yeah Elm's approach is to intentionally not have nested record updates, but I think Roc should have it

view this post on Zulip Richard Feldman (Oct 07 2024 at 18:13):

briefly, my view on it in Elm was:

view this post on Zulip Richard Feldman (Oct 07 2024 at 18:14):

I also think in terms of overall design philosophy in Roc, I'm okay with having tools that people reach for a lot when they're starting out, and then reach for less and less over time, as long as there's a reasonable path to get from the one to the other

view this post on Zulip Richard Feldman (Oct 07 2024 at 18:14):

which in the case of nested record updates, I think there is

view this post on Zulip Richard Feldman (Oct 07 2024 at 18:15):

less so if someone falls into the lens trap, but hopefully nested record updates can reduce the chances of a beginner doing that

view this post on Zulip Daniel Schierbeck (Oct 08 2024 at 10:59):

Having spent a lot of time over the last two years messing with loads of Kubernetes resources as jsonnet objects, I can certainly attest to the value of easy access to nested record fields; jsonnet is generally great for messing with objects/records, but things quickly got verbose.

view this post on Zulip Aurélien Geron (Oct 08 2024 at 20:40):

Richard Feldman said:

less so if someone falls into the lens trap, but hopefully nested record updates can reduce the chances of a beginner doing that

Pardon my ignorance, but what's the lens trap?

view this post on Zulip Jasper Woudenberg (Oct 08 2024 at 21:12):

Lenses are a DSL for defining paths into (deeply nested) datastructures, that can then be used to read or mutate whatever part of a type they point to.

So imagine you could define a path like .a[2].b (the lens), and then use that in combination with a setter function, or a getter function, or an update function, on some value of a compatible type.

I'm not entirely doing the idea full justice here, there's much wilder applications of them.

I know it primarily from Haskell, where there's a couple of libraries that offer lenses, then other libraries that use it for their APIs. For instance, the most populat AWS-wrapper library in Haskell (amazonka) uses lenses heavily, last I checked.

Here's a post with some more details if you're interested:
https://www.haskellforall.com/2013/05/program-imperatively-using-haskell.html

view this post on Zulip Richard Feldman (Oct 08 2024 at 21:43):

Aurélien Geron said:

Richard Feldman said:

less so if someone falls into the lens trap, but hopefully nested record updates can reduce the chances of a beginner doing that

Pardon my ignorance, but what's the lens trap?

the lens trap is where you use lenses

view this post on Zulip Richard Feldman (Oct 08 2024 at 21:45):

but to be less glib about it, to me lenses are basically a way of taking code that isn't structured well, making the code more complicated on top of that, and then as a bonus making it harder to restructure the code to be better in the future

view this post on Zulip Agus Zubiaga (Oct 08 2024 at 21:47):

Try searching for “lens…” on https://package.elm-lang.org :wink:

view this post on Zulip Aurélien Geron (Oct 08 2024 at 21:58):

Thanks! I'm quite new to FP, I just hadn't heard the term "lenses" before. So IIUC a lens is a tool meant to simplify accessing part of a complex data structure, but in practice they often end up making the code worse and you're better off trying to simplify the data structure or making it an opaque type with a clean interface.

view this post on Zulip Richard Feldman (Oct 08 2024 at 22:00):

that's essentially what I would say personally, but you can definitely find people who think lenses are great :big_smile:

view this post on Zulip Richard Feldman (Oct 08 2024 at 22:00):

the reason I call it a trap is that the way people who like lenses present them as like a "powerful abstraction" or something like that

view this post on Zulip Sam Mohr (Oct 08 2024 at 22:00):

No one ever said Roc wasn't opinionated

view this post on Zulip Sam Mohr (Oct 08 2024 at 22:01):

It's a good thing

view this post on Zulip Richard Feldman (Oct 08 2024 at 22:01):

so they can sound really enticing and cool, but also kind of tricky because there's new terminology and such, but then you start using them and pretty soon your code base is in trouble and now you're stuck

view this post on Zulip Aurélien Geron (Oct 08 2024 at 22:02):

python -m this | grep Flat

view this post on Zulip Aurélien Geron (Oct 08 2024 at 22:03):

Lenses make me think of OOP, where everything gets its own abstraction, and your code ends up 50 layers deep just to print Hello World.

view this post on Zulip Zeljko Nesic (Oct 10 2024 at 21:46):

Lenses and prisms are better be avoided. They are typical premature abstractions - that lead to dorky grins and beard stroking.

view this post on Zulip Kiryl Dziamura (Oct 19 2024 at 06:54):

That's the reason why I’m wary of fun and excitement in coding. Most of the exciting and fun features/syntax carry inherent complexity. Every time I hear “it's so fun to use x”, I’m thinking it should be avoided. I like to say that the actual feature of Elm is that it’s boring.

E.g. I think backpassing is fun to use because it allows me to see callbacks in a different perspective. Red flag!

Ofc I’m exaggerating. But it helps me to solve “I want to use” vs “I need to use”

view this post on Zulip Brendan Hansknecht (Oct 19 2024 at 15:47):

For myself personally, I don't care about the nested access case. I think it is fine. Using the name is fine. I rarely ever use the .field access syntax anyway.

On the other hand, nest record updates are a pain. If you want to update the inner field of a record, it is a weird double update syntax that is very verbose and less intuitive.

view this post on Zulip Brendan Hansknecht (Oct 19 2024 at 15:47):

Separately, I don't think that syntax optimizes properly

view this post on Zulip witoldsz (Oct 21 2024 at 11:20):

IMHO: one of the most nasty and terrible experience of Elm was the intentionally crippled record handling and all I ever got from the community was that using nested records is a bad design. What?

For the goodness sake, I could never understand what's wrong and how to fix the "nested record" issue. Like having a record with few separated things, like { personalData, shippingData, billingData } in a record was something "bad", because each entity has it's own structure and is sometimes even optional. And what if :scared: each of these entities had e.g. an address field? This must be a disaster.

Now it seems like it all comes back again. The darkest darkness returns :scream: Can someone, please, explain it to me, or at least point into some papers about the issue and paths to resolve, so I could finally understand?

view this post on Zulip Richard Feldman (Oct 21 2024 at 12:15):

in Roc we just haven't implemented it yet

view this post on Zulip Maksim Volkau (Nov 04 2024 at 10:08):

I have implemented nested record access as a closure shortcut in my fork, so this:

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map \rec -> rec.a.b

becomes this:

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map \.a.b

And to make it more fun, this code is for identity function :)

[{a:{b:1}}, {a:{b:2}}, {a:{b:3}}] |> List.map \.

view this post on Zulip Luke Boswell (Nov 04 2024 at 10:33):

Cool, are you exploring different designs?

view this post on Zulip Maksim Volkau (Nov 04 2024 at 21:12):

Luke Boswell said:

Cool, are you exploring different designs?

Hi, I was interested in the Roc and started digging into the codebase, because I am learning Rust as well.
Have started with parser. To be interesting, set a goal adding a feature. Something small, around a single idea. And likely went overboard :-)

I've added "closure shortcut" for a single parameter closure/lambda (not sure about the official Roc wording).

Currently, it supports the binary operators, including |>, and record, tuple access, and identity function,
and... a binary operator for when as ~ which can be combined with closure shortcut.

Examples:

List.map [1, 2, 3] \+1
List.map [{ foo: { bar: 42 } }] \.foo.bar + 5 // the lambda is \x -> x.foo.bar + 5

pipe is also a binary in Roc:

List.map [1, 2, 3] \|> Num.add 9

Binary operator for the pattern matching similar to the piping:

List.map [1, 2, 3] \x -> x ~
    1 -> 42
    _ -> 13

and as closure shortcut

List.map [1, 2, 3] \~
    1 -> 42
    _ -> 13

There is also a thing you can do to expand the closure if needed. You put space after slash and 'roc format' will expand the code into the full closure:

List.map [1, 2, 3] \ * 2 + 1 // check the space after \

on format expands into

List.map [1, 2, 3] \x -> x * 2 + 1

view this post on Zulip Sam Mohr (Nov 04 2024 at 21:33):

This is cool to see! I also find that just diving into codebases and trying stuff is one of the best ways to learn how they work, including learning the language they're written in.

This feature looks pretty cool as well. Just to set expectations on your end: we try to have Zulip discussions (usually in the #ideas channel) about new features before we consider merging them. So if you're just messing around to learn about Roc and Rust and compilers, awesome! But if you mean to add this "closure shortcut" to Roc, please consider creating a discussion topic so we can get alignment on how it'll look.

view this post on Zulip Georges Boris (Dec 27 2024 at 14:26):

I think you should also take a look at the new lambda syntax to see if it changes your experiments in any way!

view this post on Zulip Maksim Volkau (Dec 27 2024 at 19:29):

@Georges Boris Yep, it is ruining my experiments. Language moves too much at the moment, so the Parser experiments would fail by default.

view this post on Zulip Sam Mohr (Dec 27 2024 at 19:35):

The parsing will be changing a good bit for the next 3 to 6 months, it feels like


Last updated: Jun 16 2026 at 16:19 UTC