Stream: contributing

Topic: Porting parser and random


view this post on Zulip Jonathan (Jun 11 2026 at 12:15):

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.

view this post on Zulip Richard Feldman (Jun 11 2026 at 12:31):

yeah go for it!

view this post on Zulip Luke Boswell (Jun 11 2026 at 13:12):

Would love any assistance porting over roc-random, roc-parser, or any of the libraries I kind of adpted alng the way

view this post on Zulip Luke Boswell (Jun 11 2026 at 13:13):

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.

view this post on Zulip Luke Boswell (Jun 11 2026 at 13:15):

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).

view this post on Zulip Luke Boswell (Jun 11 2026 at 13:20):

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.

view this post on Zulip Luke Boswell (Jun 11 2026 at 13:21):

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.

view this post on Zulip Jonathan (Jun 15 2026 at 21:50):

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)

view this post on Zulip Jonathan (Jun 15 2026 at 21:53):

(Which is much more intuitive to me at first sight)

view this post on Zulip Luke Boswell (Jun 15 2026 at 23:00):

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.

view this post on Zulip Jonathan (Jun 16 2026 at 09:37):

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?

view this post on Zulip Luke Boswell (Jun 16 2026 at 09:52):

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.

view this post on Zulip Jonathan (Jun 16 2026 at 10:04):

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.

view this post on Zulip Jonathan (Jun 16 2026 at 13:18):

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?

view this post on Zulip Luke Boswell (Jun 16 2026 at 13:34):

Yes please, a GH issue with a minimal repro would be great :thank_you:

view this post on Zulip Jared Ramirez (Jun 16 2026 at 14:43):

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

view this post on Zulip Jonathan (Jun 16 2026 at 15:31):

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