Is there a way to control the subtyping behaviour of functions in roc?
I have the following function which is meant to be an or
operator on a decoder. Try decoderA
if that fails, then try decoderB
.
or = \decoderA, decoderB ->
\data ->
when decoderA data is
Ok a -> Ok a
_ -> decoderB data
I want the return type of this function to be c
, where:
c: [
a,
b
]
so the type annotation of or
to be:
or: JsonDecoder a, JsonDecoder b -> JsonDecoder c
When I have this type annotation I get an error telling me all my branches of my when must be the same type. Is this possible in roc or am I missing something? Cheers :)
Other type defs:
JsonDecoder t : Json -> Result t DecodingErrors
Json : [
String Str,
Number U64
...etc
]
I think because return types of decoderA
and decoderB
are different, the way to go is to use custom tags to distinguish them.
So you want the c
type to be
c: [
A a,
B b
]
and decoderA
returns [A a]
and decoderB
returns [B b]
.
@Kiryl Dziamura beat me to it but I'll leave my example here anyway :)
So, this works:
when color is
Green -> Go
Red -> Stop
But this does not:
when color is
Green -> Go
Red -> 123
But that one can be modified to:
when color is
Green -> Go
Red -> Stop 123
There isn't subtyping in Roc, tho it can look like it has. if decoderB data
doesn't return the same type as decoderA data
, it will be a type mismatch. You generally solve that by wrapping the returned values with a different tag for each different return value, so that the your function returns the union of those tags (tag union). In your example:
c: [
a,
b
]
should be:
c: [
TagFromDecoderA a,
TagFromDecoderB b
]
in the pattern match, you would apply the tag:
when decoderA data is
Ok a -> TagFromDecoderA (Ok a)
_ -> TagFromDecoderB (decoderB data)
Without the actual tag, you couldn't tell if your function returned type a
or b
. Other languages solve that by having runtime type information, but in Roc, there isn't such thing.
I haven't used decoders, but that is the general solution.
If you look at the Json tag union you pasted, it has tags "String" and "Number" (which are not some special Roc types)
Wow, you guys were fast :racecar:
thanks all for your answers! I think I was making it far more complicated than it needed to be...
What I was really wanting to do and managed to get working was:
or : JsonDecoder a, JsonDecoder a -> JsonDecoder a
or = \decoderA, decoderB ->
\data ->
when decoderA data is
Ok _ -> decoderA data
_ -> decoderB data
the way I call this function is by passing in values like this:
decoderA : JsonDecoder A
decoderB : JsonDecoder B
C : [
A,
B
]
or decoderA decoderB
when decoderA data is
Ok _ -> decoderA data
_ -> decoderB data
it seems you call decodeA data
twice. it would probably be optimized, but I'm not sure
You can also use Result.onErr
:
or : JsonDecoder a, JsonDecoder a -> JsonDecoder a
or = \decoderA, decoderB ->
\data ->
decoderA data |> Result.onErr \_ -> decoderB data
nice yeh your optimization works perfectly! cheers
Last updated: Jul 05 2025 at 12:14 UTC