# How can we simplify walks through AST?

**URL:** https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592
**Category:** Learning
**Created:** [December 12, 2025, 1:15pm UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592 "2025-12-12T13:15:39Z")
**Posts on this page:** 5
**Page:** 1

<div class="post-metadata">

### Author: ![oxcrow](https://avatars.discourse-cdn.com/v4/letter/o/d9b06d/32.png) [@oxcrow](https://discuss.ocaml.org/u/oxcrow)
#### Post date: [December 12, 2025, 1:15pm UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592/1 "2025-12-12T13:15:39Z")

</div>

Hello,

I’m writing a compiler in OCaml, and have finished implementing lexer, parser, type inference, for a small subset of my language.

Currently I’m implementing a borrow checker like Rust.

> **[GitHub - oxcrow/nxn: nxn compiler](https://github.com/oxcrow/nxn)**
>
> nxn compiler

Unlike other languages which choose to _lower_ their AST to a simpler form and then do semantic analysis on them, I’m currently doing all analysis on the raw AST, because I want to compile down to C. Not Assembly.

As of now, I have been successful, as OCaml allows us to write deeply nested mutually recursive code easily, however this has caused my design to be a _monolithic_ blob of code.

For example, the type inference itself is [[514 lines of recursive code (Github Link)]](https://github.com/oxcrow/nxn/blob/ba91a55525e273caffa2be6919402320e425fa9d/nxn/lib/main.ml#L85C1-L599C3)

While I’m okay with this for the type inference, this design has become somewhat prohibitive for more advanced semantic analysis, such as _borrow checking_, because this _monolithic_ design,

- Forces me to do all tests in a single go, and I can not run smaller passes through the AST.
- Even if I want to implement smaller passes in future, I can not do it, because this design would prohibit it.

What can I do?

**_I do not want to rewrite the “walks” through the AST. I’m looking for a “visitors” pattern solution that will help me simplify my design._**

I tried using the _visitors_ library since it seemed promising, but since my AST is somewhat complicated, it does not work _“out of the box”_, as they say.

I’m currently looking at _ppx\_deriving_ library to see if I can use its simpler _iter, fold_ methods to write a custom visitor for my AST.

_ **Do you know of any other library or tutorial that I can use to write my own custom visitors?** _

_ **Or any other sources that teaches how to simplify walks through ASTs in OCaml?** _

Thank You  
~oxcrow

---

<div class="post-metadata">

### Author: ![Kakadu](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/kakadu/32/1204_2.png) [@Kakadu](https://discuss.ocaml.org/u/Kakadu)
#### Post date: [December 12, 2025, 1:27pm UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592/2 "2025-12-12T13:27:09Z")

</div>

Did you consider some libraries for visitors pattern?  
[GT](https://github.com/PLTools/GT/) supports polymorphic variants  
[visitors](https://gitlab.inria.fr/fpottier/visitors) is somewhat faster

---

<div class="post-metadata">

### Author: ![K\_N](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/k_n/32/2793_2.png) [@K\_N](https://discuss.ocaml.org/u/K_N)
#### Post date: [December 12, 2025, 1:34pm UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592/3 "2025-12-12T13:34:28Z")

</div>

A common trick to address the mess of mutually recursive functions (`let rec type_fun ... and type_instr ... and type_expr ... and ....`) is to use open recursion (`type_fun` takes `type_instr` and `type_expr` as argument and similarly for the rest). You then have one module outside these where you tie the knot with a `let rec …. and`. If the number of function makes it impractical, using open recursion at the level of modules is also an option (these become functors) and you tie the knot with a recursive module. You may need to rework your code so that one of your module on the cycle is _safe_. I like this approach since, locally, each function looks the way it should when writing functional code, with pattern matching and “recursive” calls, just each function in its own file.

---

<div class="post-metadata">

### Author: ![EmileTrotignon](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/emiletrotignon/32/5913_2.png) [@EmileTrotignon](https://discuss.ocaml.org/u/EmileTrotignon)
#### Post date: [December 13, 2025, 9:07pm UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592/4 "2025-12-13T21:07:26Z")

</div>

I think if you truly want to visit every node (like when compiling), the visitor pattern is not that useful. Its use is more if you want to do something _for every expression_ in your AST, but you don’t care about the rest of the nodes at all.

---

<div class="post-metadata">

### Author: ![Chet\_Murthy](https://sea2.discourse-cdn.com/flex020/user_avatar/discuss.ocaml.org/chet_murthy/32/1501_2.png) [@Chet\_Murthy](https://discuss.ocaml.org/u/Chet_Murthy)
#### Post date: [December 14, 2025, 2:52am UTC](https://discuss.ocaml.org/t/how-can-we-simplify-walks-through-ast/17592/5 "2025-12-14T02:52:59Z")

</div>

I’m sorry to be unable to give you good answers to your questions. But I thought I should at least pass along the pointers that others gave me, when I asked similar questions. They pointed me at two things:

(1) in Scheme, there is a somewhat well-developed “nanopass” methodology for doing compilers as many small passes

(2) in Haskell, there’s “Scrap Your Boilerplate”

Both of these are attempts to get rid of the rote visitor boilerplate, so you can focus on the actual AST -work-. I’m not going to pretend I understood them well: I’m …. no longer “in the biz”, so when stuff gets tiresome, I just abandon it. So I didn’t get very deep into this stuff.

But it might be a place to go looking for ideas.
