I think roc does not allow me to do this :
outStruct = { inStruct : { one: 1 , two : 2 } , something = 0 }
newStruct = { outStruct & inStruct : { outStruct.inStruct & two = 9 } } it should work but apparently it does not
So two things are going on here:
First, the syntax is slightly off. The equal signs should be colons:
outStruct = { inStruct : { one: 1 , two : 2 } , something: 0 }
newStruct = { outStruct & inStruct : { outStruct.inStruct & two : 9 } }
Second, updating does not work with nested fields. I feel like it should in a case like this, maybe that is a bug that we need to file.
Specifically the issue is with { outStruct.inStruct & two : 9 }
When put in the repl directly, we print the error:
── SYNTAX PROBLEM ──────────────────────────────────────────────────────────────
This expression cannot be updated:
6│ { outStruct.inStruct & two : 9 }
^^^^^^^^^^^^^^^^^^
Only variables can be updated with record update syntax.
My gut feeling is that we should support this, but I am not sure the technical changes required for that.
A working alternative would be:
outStruct = { inStruct : { one: 1 , two : 2 } , something: 0 }
inStruct = outStruct.inStruct
newStruct = { outStruct & inStruct : { inStruct & two : 9 } }
@Folkert de Vries do you know if there is a specific technical reason that we can't support a record update like this:
{ outStruct.inStruct & two : 9 }
because it is unclear what this does
is the return type of this the type of the outer, or the inner struct
That makes sense.
To me it seems "obviously" the type of the inner struct.. I can't parse it in my head to make it be the outer one
Is it "obviously" the other way around for someone else?
To me outStruct.inStruct
is the expression between {
and &
so it's the record you're updating
Yeah, to me, that's as intuitive as the case with the inStruct
def
Elm also doesn't support arbitrary expressions in record update
and I want them all the time :D
It doesn't have to be arbitrary expressions, though. Identifiers would be enough.
I think this would be something that people would also quickly learn if we added it. Even if they thought the wrong option, they will just get a type mismatch and need to learn how this works once.
Let say there is record
P = { k: { ... } , ... } I can't do { P.k & .... } instead I have to go around f = P.k {f & ...} is that ok ?
I don't understand your workaround. Can you elaborate?
Artur Swiderski said:
Let say there is record
P = {
hm, P = {
isn't valid Roc syntax, because it's capitalized - did you mean exposing a record named k
from P.roc
?
I meant
p = { k: { ... } , ... }
-- I can't do this
h = { p.k & .... }
-- but I can
f = p.k
h = {f & ...}
-- this a bit unexpected since those are equivalent
I think this was inherited from Elm where you also can’t use arbitrary expressions in record update
That said, I’d prefer Roc supported arbitrary identifiers at least. I probably wouldn’t want to put a function call there, but being able to access record fields would be nice.
Why not allow function calls or any arbitrary expressions?
Yeah, I mean I wouldn’t be against it really. I just figured the design intention was to keep these simple.
would you want that entire { ... }
expression to evaluate to the record p
(with its k
field updated) or to the k
record?
I think we have a syntax that works for both even if it isn't currently enabled for both:
Update k in p: { p & k: {...}}
Update k only: {p.k & ...}
this returns something with the same type as k
yeah so I think that could plausibly be supported, but I remember way back when @Folkert de Vries and I were discussing the original implementation, it made something about the type inference significantly trickier. I don't remember the details anymore though. :big_smile:
Richard Feldman said:
would you want that entire
{ ... }
expression to evaluate to the recordp
(with itsk
field updated) or to thek
record?
intuitively to the 'k' record
I hope there is plan to do something about this because I stumbled upon this again
Last updated: Jul 06 2025 at 12:14 UTC