Right now in Roc, a { a, b } = capture will work for destructuring any record that has at least an a and a b field. This is concise, but is not exhaustive, so it will not lead to a type error if the destructured record adds a new field to its type.
As part of the new .. syntax proposal, we are writing open record type definitions as User a : { name : Str, age : U64, ..a }, meaning User a is an open record with extension type a. If we wanted to create a closed record here, we could substitute a with {}, so User {} : { name : Str, age : U64, ..{} }.
What do people think of requiring that ..{} at the end of record captures, meaning { a, b } = would always need to be written { a, b, ..{} } = if we wanted to only allow capturing if only the fields a and b (and no others) were present? We'd probably support { a, b, .. } = or { a, b, ..rest } = to allow ignoring extra fields.
Would you like this change? Would you hate it? Comments below!
I think it should be an optional opt in
{a, b} = ... works with any record containing a and b.
{a, b, ..{}} = works with records that only contain a and b
That said, not sold on the syntax
But I like the idea of being able to specify that the record pattern match is exhaustive
I like the proposed syntax. I can't know for sure, but I think if I encountered the new syntax without having a familiarity with Elm or Roc I might be able to grasp what the syntax means just based on seeing it used, which I don't think is true based on the current syntax.
Even though it's a breaking change, I'd prefer { a, b } to capture a closed record, and { a, b, .. } for open records
Yeah, that would be my preference as well
I'm open to try that
it certainly feels the most straightforward and most likely to catch errors
I'm personally strongly opposed to ..{} for aesthetics
But if it's the right syntax, I'll grin and bear it
But if it's the right syntax, I'll grin and bear it
To clear up the confusion; Richard is in favor of "I'd prefer { a, b } to capture a closed record, and { a, b, .. }" not for "..{}", right?
I agree, but if we change our minds, I don't wanna block progress
yeah I think we should try it the way Rust does it, e.g. make a breaking change :+1:
Great! I like type errors helping me facilitate refactors, which this helps with
Yeah, I'm for it too. A little inconvenient in function inputs, but mostly good. Also, we will require .. for open record too?
As in, would this be valid:
someFn : { a: Str b: Str, .. } -> Str
someFn = \{ a, b } ->
....
I think this is important to think about related to defaults when users don't add types
Can I pass { a, b, c } to:
someFn = \{ a, b } ->
....
Probably not, but I do think that will be the largest ramification if we require ... I think it is a very common pattern to only use a subset of record fields from a function input. Or to accept any record that happens to contain the fields you care about
Yeah, I think .. should be required to make it open even without annotations
Just want to make sure we consider this. I definitely have tons of functions in this form that I have written over the years.
Kinda makes me think about how tags are open by default in function output. Records being open by default for function input might be really useful
but tags are open by default even if you have an annotation
Sure, and records could be that way too...idk, just a general idea.
But probably a lot less useful
I can probably manage to add the 4 extra characters to like all functions on a data structure like Dict. , ..
It's good to make the default the "right" choice, and open records are a better default, probably...
Yeah, I think they might be the better default. At least for function inputs. Not sure about other locations.
Is there a way we could imply records are closed without taking the current { a, b } syntax away?
Besides { a, b, ..{} }, which I'm not a huge fan of
Flow has {| a, b |}, but I don't like it either
{ a, b, :door: }. Closed door...the perfect solution.....
I don't think I would like records in function annotations to be open by default since extra fields wouldn't produce type errors
If I remove a field from the record annotation, I want the compiler to tell me about all the calls I should remove it from
Usually,records defined in a function annotation (as opposed to in a type alias) are used as "named" arguments. Think about the positional case, wouldn't you want to get a type error if your function call is passing too many arguments?
I view it quite differently. I mostly have records that already are defined and are essentially their own types. In any given function that interacts with a type I only use a subset of the fields. So open by default makes a lot of sense. My default is extracting a subset of the fields
Also, it is no harm generally speaking if an extra field exists. Just a minor missed cleanup.
Roc could give a warning that a field is never used for that.
I remember talking about this idea with @Ayaz Hafiz at one point. I think it was in the context of tag unions becoming open by default in the output position. So basically, just to summarize what it means, it would mean that a function like this...
foo : { a : Str, b : Str } -> { a : Str, b : Str }
...is guaranteed to return a record with exactly those two fields, but it accepts records with more fields than that
and if I remember the conversation right, it seemed like it wouldn't be that useful in practice (e.g. how often do people opt into this in current Roc?) and might lead bugs to be missed somehow (although to be fair I'm not sure exactly what that would look like in practice)
I'm not a fan of ..{} because it's too subtle. It looks like you can just use { a, b, ..{c, d} }, or {a, b, ..rest }, which is likely not going to be added. It also looks like it should be a no-op just like ..[] for lists.
There's also the question whether { ..{}, a, b } would mean the same thing.
Last updated: Jun 16 2026 at 16:19 UTC