OCaml Roundup: November 2025 [ocaml-roundup-november-2025]
- December 9, 2025
- Patrick Ferris
A roundup of some of my OCaml activity this month!
Ppxlib Updates
- December 9, 2025
- Patrick Ferris
I managed to get a substantial amount of ppxlib work completed in the last month. Some of those bits are described in detail below.
Initial OCaml 5.5 Support [ppxlib-5.5-support]
- December 9, 2025
- Patrick Ferris
We recently merged 5.5 support into our main branch (thanks to Nathan for the review). It cast a light on some new features coming to OCaml including the ability to define any structure locally. Most OCaml programmers will be familiar with things like local opens (e.g. let open M in). This has now been extended to include any structure item (more or less, you cannot nest value bindings).
External types have also landed, bringing with them a new "type kind" in the AST. You can read about them in the manual.
Bug Fixes for Attribute Rewriters
- December 9, 2025
- Patrick Ferris
There was a bug in the error handling for attribute rewriters -- a relatively new feature to have been added to ppxlib.
In some places, ppxlib makes use of a With_error.t monad. Unlike the Result.t monad, With_error.t allows you to collect errors rather than find the first error and thread it through your program. This can make sense if your program can meaningfully do work after encountering an error. Unfortunately, in the logic for replacing attributes, the code could not recover from an error where there were duplicate attributes.
Value binding constraints in ppxlib
- December 9, 2025
- Patrick Ferris
I spent a good few hours understanding why the CI tests in my PR to mirage/repr were still failing. It turns out, the bug, is fairly simple to reproduce. Consider the following OCaml code:
let g : int = 1
We have a let-binding that contains a type constraint. Where, in the abstract syntax tree sense, do we attach the constraint? Since OCaml 5.1, we have quite a few options for how this can be represented.
The first is using the Ppat_constraint AST node which allows you to attach a type constraint to any pattern.
| Ppat_constraint of pattern * core_type
(* (P : T)*)
This tends to be used for things like function arguments (e.g. fun (x : int) -> x + 1). So it doesn't quite capture what we want for this binding constraint.
On older OCaml compilers, let g : int = 1 also includes a Pexp_constraint on the body of the value binding. This makes sense if we consider a more general case of the syntax where we allow functions.
let g x : int = x + 1
In this example, we no longer parse a Ppat_constraint and instead this is treated as syntactic sugar for:
let g x = (x + 1 : int)
In OCaml 5.1 a new field for value bindings was added allowing type constraints to be tracked directly in the bindings themselves.
and value_binding =
{
pvb_pat: pattern;
pvb_expr: expression;
pvb_constraint: poly_constraint option;
pvb_attributes: attributes;
pvb_loc: Location.t;
}(** [let pat : type_constraint = exp]*)
Our type constraint (: int) can be recorded in the pvb_constraint directly. This is a nice change, bringing the concrete syntax of the language closer to the parsetree. For ppxlib, however, it does create a bit of a headache as we now have to encode the different versions of expressing type constraints into the AST of the compiler before 5.1!
So what was the bug? The author's of repr had added some value bindings to circumvent some "unused value warnings" in OCaml. They choose to write these bindings like:
let (_ : int) = 1
Here, I have simplified the types and the body of the binding. The significant thing to notice is the parentheses surrounding the value binding's pattern. This forces the AST representation to be a single Ppat_constraint node regardless of the current compiler version. No Pexp_constraint and no pvb_constraint.
However, using ppxlib with a compiler less than 5.1 meant that this did not roundtrip cleanly. Instead, the value after passing through ppxlib's migrations was:
let _ : int = 1
After bottoming out at the issue, I discovered we already had a test case for this in ppxlib! Whatsomore, ocamlformat.0.20.0 (which repr was still using) actually transforms instances of let x : t = ... into let (x : t) = ...! This limitation does not seem inherent to the parsetree, the printer or the migrations. But I have asked the (very kind and friendly) previous maintainers about these changes.
Outreachy
- December 9, 2025
- Patrick Ferris
December 8th 2025 marks the start of the next round of Outreachy. I have been preparing some issues for geocaml/ocaml-tiff and I am excited to get started with Tambe Salome!
Graft and Bib [graft-and-bib-update]
- December 9, 2025
- Patrick Ferris
I spent some time at the start of the month updating and fixing Graft and Bib.
I added named subtrees to the markdown syntax. This allows users to create subtrees with headings that are also externally linkable.
With some more bug fixing, I hope to release both libraries before the new year!