I'm looking into migrating some libraries to the new compiler, starting with roc-parser and unicode. Most of it looks fairly straightforward, but I'm not entirely sure how to reorganize modules. For example, in roc-parser, the Xml module currently starts like this:
module [Xml, XmlDeclaration, XmlVersion, Node, Attribute, xml_parser]
import Parser exposing [Parser, const, map, skip, keep, one_or_more, one_of, many, between, alt, chomp_while, flatten, lazy, chomp_until]
import String exposing [parse_str, string, Utf8, digits, codeunit_satisfies]
Xml : {
xml_declaration : [Given XmlDeclaration, Missing],
root : Node,
}
XmlDeclaration : {
version : XmlVersion,
encoding : [Given XmlEncoding, Missing],
}
XmlVersion := {
after_dot : U8,
}
implements [Eq]
v1_dot0 : XmlVersion
v1_dot0 = @XmlVersion(
{
after_dot: 0,
},
)
...
expect ...
...
xml_parser : Parser Utf8 Xml
xml_parser =
const(
|xml_declaration|
|root| {
xml_declaration,
root,
},
)
|> keep(p_prolog)
|> keep(p_element)
|> skip(many(p_whitespace))
...
Should I migrate it to something like this?
import Parser exposing [Parser, const, map, skip, keep, one_or_more, one_of, many, between, alt, chomp_while, flatten, lazy, chomp_until]
import String exposing [parse_str, string, Utf8, digits, codeunit_satisfies]
Xml := {
xml_declaration : [Given(XmlDeclaration), Missing],
root : Node,
}. {
XmlDeclaration : {
version : XmlVersion,
encoding : [Given(XmlEncoding), Missing],
}
XmlVersion := {
after_dot : U8,
}.{
is_eq : XmlVersion, XmlVersion -> Bool
is_eq = |v1, v2| v1.after_dot == v2.after_dot
}
v1_dot0 : XmlVersion
v1_dot0 = { after_dot: 0 }
...
xml_parser : Parser(Utf8, Xml)
xml_parser = {
const(
|xml_declaration| {
|root| { {xml_declaration, root } }
}
)
.keep(p_prolog)
.keep(p_element)
.skip(many(p_whitespace))
}
...
}
expect { ... }
Specifically:
Xml) must always be defined with := or ::. In this case, I've migrated it from a type alias to a type declaration. Is that correct?implements [Eq] no longer exists so we must implement is_eq manually? Or is a new syntax coming?|root| { {xml_declaration, root } }XmlVersion and XmlDeclaration inside of Xml, perhaps we should rename them to Version and Declaration to avoid the repetition?I can answer a couple I think
» f = |a, b| {a, b}
assigned `f`
» f(1, 2)
{ a: 1.0, b: 2.0 }
Personal preference
I was also looking into migrating roc-parser but was blocked by #9670 last I checked. It did seem like much of it could be migrated over quite mechanically besides some cases I hit where the return type had to be decoupled from the input, e.g.
match result {
# this
Ok({key1, key2}) => Ok({key1, key2})
# but not
Ok(_) as ok_res => ok_res
}
which also seemed to affect map_ok idioms.
You will be much faster than me and I didn't really have any major API suggestions either (besides maybe some conveniences over curried const arguments and keep/apply redundancy), but I'd be glad to help porting libraries needed for Exercism as well.
I can open a draft PR for roc-parser if you'd like to track progress, but I'm conscious that this is a blocker for you and I haven't been finding much time recently.
It looks like @Jared Ramirez has this fixed in https://github.com/roc-lang/roc/pull/9688 maybe... but that just needs updating / review
I think I missed that... and I'm travelling for work for a couple of days so I probably wont be able to land that until later in the week.
@Anton would you be able to look at Jared's PR?
thanks Anton! that's ready to go once CI finishes (usually by the time CI finishes, something else has landed the branch gets out of date, then I rebase and re-run CI, and around we go haha)
Jonathan said:
I can open a draft PR for roc-parser if you'd like to track progress, but I'm conscious that this is a blocker for you and I haven't been finding much time recently.
Oops, I just saw your message now.
I've opened https://github.com/lukewilliamboswell/roc-parser/pull/30
Didn't want to leave a review comment because it felt a bit too close to personal preference :laughing: but one difference I noticed was if in some cases reducing the curried function nesting might help communicate the intent better. For instance with between I find that putting the const selector in one line shows the dropping of the first and last parse results.
between = |parser, open, close| {
const(|_| |val| |_| val)
.apply(open)
.apply(parser)
.apply(close)
}
Again that may just be my preference, and I'm not up to date with the evolving Roc style either.
Last updated: Jul 23 2026 at 13:15 UTC