Does Roc intentionally not include a Maybe/Option builtin? Is that better expressed as a raw tag union? The only language I've used with a builtin for this is TypeScript, but it feels very useful.
or is Result intended to be used for this (despite a Maybe/Option not always corresponding to error handling)
I think the second half of this section addresses your question https://github.com/rtfeldman/roc/blob/trunk/FAQ.md#why-doesnt-roc-have-a-maybe-or-option-or-optional-type-or-null-or-nil-or-undefined
To describe something that's neither an optional field nor an operation that can fail, an explicit tag union can be more descriptive than something like Maybe. For example, if a record type has an artist field, but the artist information may not be available, compare these three alternative ways to represent that:
artist : Maybe Artist
artist : [Loading, Loaded Artist]
artist : [Unspecified, Specified Artist]
All three versions tell us that we might not have access to an Artist. However, the Maybe version doesn't tell us why that might be. The Loading/Loaded version tells us we don't have one yet, because we're still loading it, whereas the Unspecified/Specified version tells us we don't have one and shouldn't expect to have one later if we wait, because it wasn't specified.
Naming aside, using explicit tag unions also makes it easier to transition to richer data models. For example, after using [Loading, Loaded Artist] for awhile, we might realize that there's another possible state: loading failed due to an error. If we modify this to be [Loading, Loaded Artist, Errored LoadingErr], all of our code for the Loading and Loaded states will still work.
In contrast, if we'd had Maybe Artist and were using helper functions like Maybe.isNone (a common argument for using Maybe even when it's less self-descriptive), we'd have to rewrite all the code which used those helper functions. As such, a subtle downside of these helper functions is that they discourage any change to the data model that would break their call sites, even if that change would improve the data model overall.
On a historical note, Maybe may have been thought of as a substitute for null references—as opposed to something that emerged organically based on specific motivating use cases after Result already existed. That said, in languages that do not have an equivalent of Roc's tag unions, it's much less ergonomic to write something like Result a [ListWasEmpty]*, so that design would not fit those languages as well as it fits Roc.
Hmmm yeah but I don't have a .map or a .withDefault that operates on Unspecified or Specified Artists.
yeah, my thinking is that if you find yourself wanting map or withDefault then you probably want Result
but I could be wrong!
part of the motivation here is to do an experiment to see how it is in practice
I have this intuition that not including Maybe will lead to nicer APIs and data modeling in practice, but the only way to test that experimentally is to try it
obviously if it ends up being worse in practice, it's easy to add Maybe to the language after the fact
but so far I've been liking it better personally
Richard, what do you think will happen with JSON decoders and null values? It seems natural to think that auto-derived decoders will map nullable values to something like JsonNull | JsonValue val and then there will be a library of functions like withDefault for operating on that tag union. Result semantically doesn't feel right for that case
I think it's fine if a JSON library exposes Nullable - there's a relevant distinction between "this field might be null" and "this field might be missing" and it's always bothered me that Maybe didn't distinguish between those :sweat_smile:
Sounds great! I forgot about that FAQ, thanks for the reminder, y'all :)
It's also a good reminder that when I search for prior knowledge/communications about a topic, I need to search
a) TUTORIAL.md
b) FAQ.md (forgot this step this time)
c) Zulip
d) roc-for-elm-programmers.md (I sometimes find good stuff in here)
I don't think this is too many documents/locations, and I'm glad that they're all linked in the first paragraph of the README.md
I think my biggest worry is that in the same way it makes sense for a JSON library to expose that, it might make sense for lots of different things to invent their own Maybe type, maybe with slightly different utility functions, and then it won’t be quite as easy to add Maybe to the language later.
yeah I thought about that, but I couldn't think of any examples from my Elm experiences other than Nullable where Maybe was being returned and Result wouldn't have worked
Is it possible to define a Result with an empty error union? If so, it would work as a maybe and be able to use the result mapping functions.
I guess it would read a bit weird.
well Result x [] is just Ok x :big_smile:
Result x [OneThingOnly] is more like Maybe
e.g. List.first : List elem -> Result elem [ListWasEmpty]*
Ah, fair
Result x [] is by the way very useful to have; it lets you use stuff that is infallbile in places where results would be expected, and you won't have to care about the error path at all :ok:
it _ should _ even collapse to just x at runtime, though I haven't gotten that to work yet
Last updated: Jun 16 2026 at 16:19 UTC