I tried using Dict.empty() as the default value in a record:
main! = |_| {
rec : { foo : Dict(U64, U64) ?? Dict.empty() }
rec = {}
dbg rec
Ok({})
}
Unfortunately, I get the following error:
── ✗ default value must be a literal ───────────────────────────────────────────────────────────── test_default.roc:2:37
The default value for the foo field is not a literal.
rec : { foo : Dict(U64, U64) ?? Dict.empty() }
^^^^^^^^^^^^
A field default (??) is materialized by the compiler at every construction site that omits the field, so it must be a literal: a number, an interpolation-free string, a tag, or a list, record, or tuple built only from literals. Anything that
refers to another value could form an evaluation cycle the compiler will not chase.
The error is pretty clear, but I'm wondering whether there might be a workaround? Maybe some way to force the compiler to evaluate Dict.empty() at compile time, or maybe another way to build an empty Dict?
I guess I could make the field entirely optional, then default to Dict.empty() whenever I try to access it:
main! = |_| {
rec : { foo ?: Dict(U64, U64) }
rec = {}
rec_foo = rec.?foo ?? Dict.empty()
dbg rec_foo.len()
Ok({})
}
I guess that's ok, but I'm still wondering whether there's another way?
I really haven't explored this new optional record feature at all
Interestingly, the following code fails as well, but if I replace MySet.empty with MySet.([]), then it works.
MySet(a) :: List(a).{
empty = MySet.([])
}
main! = |_| {
rec : { foo : MySet(U64) ?? MySet.empty }
rec = {}
dbg rec
Ok({})
}
I also ran into an issue with a List of records containing default fields when they don't all use the same set of fields. I filed issue #10886.
this smells like python default args being static - fight it with fire! at least in Roc they are immutable, to it's not a problem
IMO your first version should be acceptable - I wonder what it is about Dict.empty() that makes it not a literal
it's just a limitation of the initial implementation of optional record fields - @Jared Ramirez commented on another thread that arbitrary expressions should work in the future :smile:
yup! i have a PR that’s almost ready that relaxes this restriction!
Btw, Dict and Set were missing to_inspect, so I just added them in PR #10900: can someone please review it?
The output format looks like "Set.from_list([4,3,2,1])". I think it's useful to be able to copy/paste the Str.inspect output to the REPL.
I vaguely remember we did not want people to depend on the order of the output.
Is it ok to add to_inspect to Dict and Set @Richard Feldman?
yep! fold and iter for those have a deterministic order based on insertion order, regardless of hashing function
so I think it should even still be the case that if we make the output be "Set.from_list(...)" and then you put that Set.from_list call back into the repl, it should give the same output
Yeah, I was wondering about that too. I just checked: it looks like the order remains the insertion order even after removing/inserting items. I thought that this might mess up the order, but it doesn't.
yeah that's very much by design! :smiley:
Last updated: Sep 03 2026 at 15:16 UTC