Suppose I have an opaque Xml type like so:
Xml :: Str.{
concat : Xml, Str => Xml
concat = |prev, next| prev.concat(next)
}
And I want to define an Html := Xml type on top, how do I unwrap Html to obtain the underlying Xml?
Some things I tried that don't work:
# Doesn't unwrap the `Html` and returns a Html != Xml type-error
body : Html -> Html
body = |prev| match prev {
xml => Html.(xml.concat("<body/>"))
}
# Syntax error
body : Html -> Html
body = |prev| match prev {
Html.(xml) => Html.(xml.concat("<body/>"))
}
I can work around this by defining instead Html := { xml: Xml } and destructuring the record.
I'd like for that last example to work (and also destructuring right in the argument, e.g. |Html.(xml)| but I don't think @Jared Ramirez has gotten to that yet :smile:
destructuring via Nominal.(val) just landed in https://github.com/roc-lang/roc/pull/9851
should now be able to do:
Html :: Str. {
body : Html -> Html
body = |Html.(xml)| Html.(xml.concat("<body/>")
}
I'm having a bit of trouble with something similar while porting roc-pg.
I have the following opaque type:
Decode(value, err) :: List(U8) -> Try(
{
decoded : value,
remaining : List(U8),
},
err,
)
and in the associated items block I have this function:
await : Decode(a, err), (a -> Decode(b, err)) -> Decode(b, err)
await = |Decode.(decoder_a), callback| Decode.(
|bytes| {
a = decoder_a(bytes)?
Decode.(decoder_b) = callback(a.decoded)
decoder_b(a.remaining)
},
)
So I'm trying to unwrap the function within the opaque wrapper and then call it. Is there a different syntax I should be using for this?
(Here's the old Roc code for reference)
await : Decode a err, (a -> Decode b err) -> Decode b err
await = |@Decode(decoder_a), callback|
@Decode(
|bytes|
Result.try(
decoder_a(bytes),
|a|
@Decode(decoder_b) = callback(a.decoded)
decoder_b(a.remaining),
),
)
It also doesn't work if i skip the unwrap attempt decoder_b = callback(a.decoded) or if I attempt to remove the outer wrapping.
that looks right to me - any ideas @Jared Ramirez? :thinking:
It looks like the issue is with this line:
Decode.(decoder_b) = callback(a.decoded)
Using a match instead works fine:
await : Decode(a, err), (a -> Decode(b, err)) -> Decode(b, err)
await = |Decode.(decoder_a), callback| {
Decode.(
|bytes| {
a = decoder_a(bytes)?
match callback(a.decoded) {
Decode.(decoder_b) => decoder_b(a.remaining)
}
}
)
}
Ok, I can use a match for now and revisit if/when the other syntax becomes possible :slight_smile:
looks like a parser issue! Will check it out
fix is up here: https://github.com/roc-lang/roc/pull/9900
Last updated: Jul 23 2026 at 13:15 UTC