Parz v1 finally published!

25 June 2026

Updated: 25 June 2026

I’ve been writing a LOT of Gleam recently. So it felt like it was probably time for me to get my parser combinator library (aka. parz) into a fully usable state

Since I wrote it back in 2024 (wow that long ago huh?) I hadn’t really had a need for it. Recently I wanted to write some small parsers to help me with my site but the library was still missing one thing that would make it actually sorta useful - recursive parsing

The actual implementation isn’t really difficult, it’s basically just defining a combinator that delays actually calling a parser which then lets a parser reference itself, for example:

1
/// Takes a thunk that will be lazily evaluated to a parser. This makes it
2
/// possible to define recursive parsers
3
pub fn lazy(
4
thunk: fn() -> Parser(a),
5
) -> fn(String) -> Result(ParserState(a), String) {
6
fn(state) { thunk()(state) }
7
}
8
9
// using it then looks something like this
10
fn group() {
11
lazy(fn() {
12
between(str("("), choice([constant(), group()]), str(")"))
13
// ^ we can reference ourself in here
14
})
15
|> map(Group)
16
}

However, doing this meant that all parsers would have to be made top-level functions since Gleam doesn’t allow for recursion internally

I tried a few variations of the API that would have let me do something like this but they all ended up depending on some weird fallback behavior. Even then - you’d still have to resort to globals eventually if you depend on any function other than the one being defined - again, due to how local values and functions work in gleam

This isn’t bad, per se, just not what I had in mind when I designed the API initially

But I’m fine with it now, it’s only a little different to my ideal version but works well within the constraints of the language and I think parsers are pretty neat with it

Here’s a full example of a parser for parsing simple paths using parz:

1
import parz.{run}
2
import parz/combinators.{map, separator}
3
import parz/parsers.{regex, str}
4
5
type Path {
6
Path(List(String))
7
}
8
9
// parsers are defined at the top level to ensure recursion is
10
// possible if needed
11
fn segment() {
12
// a parser can be made with a pre-defined base parser from `parz/parsers`
13
regex("\\w+")
14
}
15
16
// a more complex parser can be defined by combining other parsers from `parz/combinators`
17
fn parser() {
18
separator(segment(), str("/")) |> map(Path)
19
}
20
21
pub fn main() {
22
let result = "my/example/path" |> run(parser())
23
// do something with the parsed output
24
}

I think that’s not too shabby

As it stands right now, I’d like to add some more combinators and possibly faster non-regex based string parsers - but since this is mostly just a learning project I’m actually pretty happy it’s reached this level of usability. I’ve even managed to implement a pseudo JSON parser using which is good enough for anything I’m likely to actually use it for

In conclusion, my little library has been promoted to v1, go and check it out!

And if you haven’t already - definitely look at the Gleam programming language - it’s great!