I've begun to want to write some random scripts during work in Roc, and often it involves "parsing" insofar as trimming leading numeric prefixes from a string. I understand that the stance is to lean on a more sophisticated parser for this (though I feel that -- being so simple and quick n dirty -- it is an excellent use case for something along the lines of Regex.trim_prefix), so unless someone else is working on it, would it be a good candidate for a relative novice/intermediate to tackle porting?
The same applies to roc-random, which would be useful for testing and benchmarking algorithms and datastructures in roc.
yeah go for it!
Would love any assistance porting over roc-random, roc-parser, or any of the libraries I kind of adpted alng the way
There is a lot of opportunity to improve those, not just port 1-1 -- so if you are interested in the API design aspect there is lots of experiments and things to try out.
They don't need to be perfect or anything... even just something rough as a first pass is better than nothing and at this stage would really help to find issues early or validate different design assumptions (like the way apps/packages/platform work together).
Also FYSA for anyone interested... there is a test package that is a direct port of the roc-parser core in the repo -- https://github.com/roc-lang/roc/blob/main/test/package_simple_parser/Parser.roc
We were using that to help validate the lambda sets implementation.
Now that I'm looking at that... the comment about being blocked is no longer valid, and so we should be good to add more combinators and capability to that.
Luke Boswell said:
Now that I'm looking at that... the comment about being blocked is no longer valid, and so we should be good to add more combinators and capability to that.
Was this the limiting factor for why you went without apply, const etc in the version embedded in roc? There seems to be a difference in API too. E.g., keep in roc-parser is
keep : Parser(input, (a -> b)), Parser(input, a) -> Parser(input, b)
and in the embedded module
keep : Parser(input, a), Parser(input, b) -> Parser(input, b)
(Which is much more intuitive to me at first sight)
The version in roc-parser was written by hand and battle tested.
The version in the repo was written to help flush out bugs and help us validate the machinery for compiling this kind of thing. It would be good to make it as realistic as possible so we can be more confident that everything works correctly until a real parser package gets published.
So this is most likely detail that is overlooked, not a deliberate design or anything, and I wouldn't be surprised if the version in the roc tests needs some love.
Got it. I'm not well versed in parser combinators, so to clarify - it's my understanding that the first style is close to applicative parsers, and is done that way so that the api is convenient to use with record builders?
I'm not sure about the difference -- it wasn't an intentional design change or anything. The limiting factor for not including the other combinators (or using the same shape) in the version in roc repo was just that there were type system bugs that prevented it. No one has gone back and updated it since.
Just had a penny-drop moment :slight_smile: The version in the repo is simpler and similar to what I'm used to / expect, but the roc-parser uses curried functions to thread accumulated data through the parser, much cooler :smile: I'll get back to you when I'm a bit further along.
I'm getting a type mismatch error where the displayed types appear equal. I can't seem to get this to work (split up and annotated for better errors):
sep_by : Parser(input, a), Parser(input, sep) -> Parser(input, List(a))
sep_by = |parser, separator| {
co : Parser(input, List(a))
co = const([])
sb1 : Parser(input, List(a))
sb1 = sep_by1(parser, separator)
alt(sb1, co)
}
-- TYPE MISMATCH ---------------------------------
This expression is used in an unexpected way:
┌─ /Users/jrp2018/repos/roc-parser-fork/package/Parser.roc:385:15
│
385 │ sb1 = sep_by1(parser, separator)
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^
It has the type:
Parser(input, List(a))
But the annotation say it should be:
Parser(input, List(a))
I'm imagining this has something to do with polarity or generalisation? So far I've been able to circumvent similar issues by avoiding ? and making sure to re-wrap return values. Practically, this involved changing the contents of apply from
{ val: fun_val, input: rest } = fun_parser.parse_partial(input)?
parse_partial(val_parser, rest).map_ok(
|{ val: val, input: rest2 }| { val: fun_val(val), input: rest2 }
)
to
match fun_parser.parse_partial(input) {
Err(msg) => Err(msg)
Ok({val: fun_val, input: rest}) => {
match parse_partial(val_parser, rest) {
Ok({val, input: rest2}) => {
Ok({ val: fun_val(val), input: rest2 })
}
Err(msg2) => Err(msg2)
}
}
}
(map_ok seemed to cause an issue as well as ?: the callsite of apply would have a similar error as shown above).
In this case I thought it could be related to the "floating"/unpinned input parameter of the returned Parser from const
const : a -> Parser(_input, a)
but creating const_alt : a, Parser(input, x) -> Parser(input, a) in order to use the dummy parser supplied as argument to 'connect' the inputs also didn't help (making it very clear I have no idea how inference works :smile:). It's been a bit messy trying to reduce this but I can supply a minimal example in an issue if something isn't already in the works?
Yes please, a GH issue with a minimal repro would be great :thank_you:
I can look into this once the minimal repro is up! I suspect this is in a similar vein to some of the other type issues we've been seeing around local decls with rigid vars
Couldn't figure out how to reduce it closer to the underlying problem, but stripped out the rest so hope it isn't too tricky to work with https://github.com/roc-lang/roc/issues/9670
Last updated: Jun 16 2026 at 16:19 UTC