OCaml Roundup: March 2026 [ocaml-roundup-march-2026]
- March 10, 2026
- Patrick Ferris
A POSIX Shell in OCaml [weekly-2026-w12]
- March 19, 2026
- Patrick Ferris
Long time, no weekly. Since the start of this year I have been building a POSIX shell in OCaml called msh (with the underlying library called Merry). Merry is available online now.
A POSIX(ish) shell in OCaml
- March 19, 2026
- Patrick Ferris
Merry is a POSIX(ish) in OCaml. It uses Eio alongside Morbig (a static parser for POSIX shell).
Why another (POSIX) shell?
- March 19, 2026
- Patrick Ferris
Shells have been around for a long time. In my research on the notion of metashell I wrote about Louis Pouzin originally coining the term:
Metashells › Louis Pouzin's "SHELL" [pouzin-shell]
- May 19, 2025
- Patrick Ferris
I spent some time reading part of the multics design documentation this week. Louis Pouzin coined the term "SHELL" in this document, and I was reminded yet again just how important it is to be a good writer even as a "computer science researcher". For example, this excerpt from the requirements section of the document
The previous definitions imply that a command MUST be designed while keeping in mind the user, sitting at his console, wondering about what might be going on, mistyping or forgetting arguments, even if fully aware of the conventions, and possibly interfering with the command by hasty quits, carriage returns, and other temperamental reactions.
And then later, when defining the "SHELL".
We may envision a common procedure called automatically by the supervisor whenever a user types in some message at his console, at a time when he has no other process in active execution under console control (presently called command level). This procedure acts as an interface between console messages and subroutine. The purpose of such a procedure is to create a medium of exchange into which one could activate any procedure, if it were called inside of another program. Hereafter, for simplification, we shall refer to that procedure as the "SHELL".
It still surprises how little the undergraduate degree in computer science at Cambridge focuses on writing skills.
I built Shelter exploring the idea of a shell-like interface that allowed users to undo their shell actions (amongst other cool tricks). Unfortunately shell-like is not enough. Shelter cut many corners to masquerade as a shell (e.g. appending env to understand how a command may have altered the execution environment). I felt it was necessary to make Shelter a "SHELL"! To do that, I needed a solid foundation to build on.
msh, the POSIX shell that comes with Merry, is by no means POSIX-complete in terms of features. But it is my daily driver at this point. It includes a pure OCaml rewrite of linenoise (a small, self-contained alternative to the venerable readline) called bruit.
If you have docker installed on your machine, you can take it for a spin today:
docker run -it --rm patrickferris/msh
The patrickferris/msh docker image is just for trying it out. It is based on the OCaml 5.3 alpine image.
Alternatively, you can build msh from source and have it available in your opam switch.
opam pin git+https://tangled.org/patrick.sirref.org/merry
There are many small paper cuts left to patch over, but most of it is porcelain (e.g. ctrl+left-arrow for moving in bruit). Unfortunately, these are the kinds of things that you will immediately stumble upon.
What makes Merry different?
- March 19, 2026
- Patrick Ferris
Nothing.
Merry is supposed to be a solid, POSIX-ish base to build on. Unfortunaltely, as it turns out, the subset of features from the POSIX specification that people actually use... is pretty much all of it. Every possible redirection, variable expansion, shell built-in and compound command make some appearance. Not to mention the non-POSIX bits of shell we all take for granted (e.g. &>-redirection).
Merry and msh are useable. You will most likely find bugs if you use them. If, when using msh, you find something obscure happening you can enable debug mode either my setting the variable MSH_DEBUG or by invoking msh with -v -v.
What's next?
- March 19, 2026
- Patrick Ferris
It is soon time to combine Shelter and Merry into the time-travelling, POSIX-ish shell that I have been trying to build since I first started working on Shelter.
For this to be successful, I need Merry to be able to pass plenty of tests and right now that involves trying to install plenty of packages using tools like apk and apt.
Shell MRDT
- March 19, 2026
- Patrick Ferris
What I am particularly interested in reasoning about, is the execution context in Merry. This value, alongside the file-system, constitutes a fairly deep understanding of the state that changes in each step of a shell's evaluation loop.
This was, in terms of Shelter, the missing piece for truly building some kind of MRDT across shell sessions.
TIFF in OCaml
- March 19, 2026
- Patrick Ferris
I picked up from the excellent Outreachy work of Tambe Salome in getting write-support in ocaml-tiff. We are getting closer to the kind of API I envisaged in this latest round of refinement and review.
let ( / ) = Eio.Path.( / )
let checkerboard ~size =
let v = Nx.zeros Nx.uint8 [| size; size |] in
for row = 0 to size - 1 do
for col = 0 to size - 1 do
if (row + col) mod 2 = 0 then Nx.set_item [ row; col ] 254 v
done
done;
Nx.to_bigarray v
let () =
Eio_posix.run @@ fun env ->
Tiff_eio.with_open_out (env#cwd / "example.tiff") @@ fun w ->
let tif = Tiff.make (checkerboard ~size:256) in
Tiff.to_file tif w

I have also been extremely pleased to see further external collaborators appearing:
Ppxlib release and Merry updates [weekly-2026-w13]
- March 30, 2026
- Patrick Ferris
Ppxlib Releases › 0.38.0 [ppxlib-release-0-38-0]
- March 30, 2026
- Patrick Ferris
Nathan and I released ppxlib.0.38.0 last week. Its main feature is full migration support for the upcoming OCaml 5.5 compiler (currently in its alpha3 release). This means supporting the handful of new features landing in OCaml 5.5: modular explicits, external type declarations and arbitrary "local" structure items.
As Nathan and I continue to find a plausible maintenance story for ppxlib, Nathan has opened an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language.
This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.
There are also some nice bug fixes in there too:
A potential OOM loop has now been removed.
Merry updates
- March 30, 2026
- Patrick Ferris
It turns out writing a POSIX(ish) shell is hard; at least, there is a vast number of slightly obscure features that one needs to support. I think this is surprising because most people writing shell scripts write simple shell scripts; scripts that use a much smaller subset of features. In the same breath, those same developers are likely to use something like apt-get install bash which runs a plethora of more advanced (and non-POSIX) shell scripts!
Exec redirects
- March 30, 2026
- Patrick Ferris
In a shell script, you can use the built-in command exec to replace the current process with a new one (e.g. exec vim). However, there is a different mode of operation for exec:
If exec is specified with no operands, any redirections associated with the exec command shall be made in the current shell execution environment.
One of my litmus tests for Merry is the Debian debootstrap scripts (h/t Anil). One thing that it does is the following:
err () {
printf "err $1" >&4
}
exec 4>&1
A lot of detail has been elided for clarity. exec 4>&1 sets up a redirection for the shell's execution environment in which file descriptor 4 is now an alias for standard output. So, writing to 4 (by redirecting a command's standard output to 4 i.e. >&4) will output to where standard output is going (most likely the terminal).
For this to work, shell's must dup2 the relevant file descriptors which have the following condition:
If the file descriptor newfd was previously open, it is closed before being reused; the close is performed silently (i.e., any errors during the close are not reported by dup2()).
This is all very well, unless your program has an important file already open that happens to have file descriptor 4. The Eio Linux and POSIX backends suffer from this problem. Both make use of a file-based synchronisation mechanism for waking up the eventloop should another domain push a completion to the scheduler's run queue. On Linux this is via an eventfd and in POSIX, a pipe.
This dup2 will close the eventfd and will likely grind Eio to a halt (or an assert false). For now, I have resorted to vendoring Eio and moving the eventfd file descriptors to higher values, though I have opened a PR to make this more configurable. This bug is quite easy to write up... it was not so easy to find!
As a random example, consider the debconf/confmodule script which offers very little room for a buggy implementation!
Outreachy
- March 30, 2026
- Patrick Ferris
The presentations from the demonstrations for this round of Outreachy are now online!
Write support in OCaml TIFF library [outreachy-ocaml-tiff]
- February 9, 2026
- Patrick Ferris
I am mentoring Tambe Salome during the December 2025 Outreachy round to add support for writing TIFF files in the ocaml-tiff library.
You can now see the video of the demonstration day presentation:
Forking in Shells & Library Maintenance [weekly-2026-w14]
- April 3, 2026
- Patrick Ferris
This week, Merry got close to executing apt-get install ca-certificates and running Debian's debootstrap scripts correctly. Nathan and I released Ppxlib 0.38.0, I added support to ocaml-proj for compiling to the browser and I fixed a long-standing bug in hilite.
Forking in Merry
- April 3, 2026
- Patrick Ferris
So far, Merry has managed to support many features of the POSIX shell specification without needing to do a fork(2) without an exec. Or, to put it another way, all the forking that Merry does, will only be followed by running C code. This is actually a feature of Eio's Process API. It is required to make process execution safe in the context of multiple domains in OCaml (mostly due to ensuring consistency within the garbage collector). However, it is perfectly okay to fork and run OCaml code provided there is only a single domain.
In a shell, many features of the shell language require that commands (or built-ins, function applications etc.) execute within a subshell. The specification does not go into detail about how shells should implement this, but many choose to fork in order to preserve some state in the parent (for example, the file descriptor table). Some shells try to minimise the number of forks as an optimisation. The korn shell (ksh) is one such shell.
Using the notation
$(command)will causecommandto execute in a subshell of the current ksh. In many instances, ksh will not actually fork/exec a subshell when command is a built-in or a shell function.
By trying to adhere to Multicore OCaml's "thou shalt not fork" commandment, Merry is much more similar to ksh in this regard. However, whenever there is an interaction with a shell built-in and semantics that need a child process, things get tricky very quickly. Consider the following:
while echo "hello"; do
:
done | head -n 3
We have a mix of shell built-ins, normal commands, a compound command (the while loop) and a pipeline. Each individual command in the pipeline requires you to run them in a subshell and set them up before executing them. Without a fork for that first command, you may end up looping forever which is not the intended behaviour here. I had a good conversation with Michael about the implications of this for reproducibility.
For now, I have started implementing a fork for some of the shell features whilst still trying to maintain the functional core of the implementation.
An OCaml 5.5 compatible Ppxlib
- April 3, 2026
- Patrick Ferris
Nathan and I released an OCaml 5.5 compatible version of ppxlib.
Ppxlib Releases › 0.38.0 [ppxlib-release-0-38-0]
- March 30, 2026
- Patrick Ferris
Nathan and I released ppxlib.0.38.0 last week. Its main feature is full migration support for the upcoming OCaml 5.5 compiler (currently in its alpha3 release). This means supporting the handful of new features landing in OCaml 5.5: modular explicits, external type declarations and arbitrary "local" structure items.
As Nathan and I continue to find a plausible maintenance story for ppxlib, Nathan has opened an issue on the OCaml compiler to discuss the idea of adding additional extension points to the language.
This comes from the desire to be able to store encoded versions of new OCaml features inside older abstract syntax trees.
There are also some nice bug fixes in there too:
A potential OOM loop has now been removed.
Browser support for ocaml-proj [ocaml-proj-virt]
- April 3, 2026
- Patrick Ferris
A while ago I built some "modern" bindings to PROJ4 in OCaml. After reading Jon Ludlam's weeknotes (and speaking with him and Anil, I thought it might be nice to add a Javascript backend to those bindings). This was relatively straight-forward using Dune's virtual libraries and is available on Github.
With virtual libraries, your own data analysis could (if you wished) depend solely on the proj library and later choose to either link it with proj.c or proj.js depending on where the analysis is being deployed.
I briefly looked at WASM support, but quickly realised there was not much appetite for it and trying to compile around the C FFI was going to be hard.
Hilite updates
- April 3, 2026
- Patrick Ferris
Since I helped relaunch the ocaml.org website a few years ago, I have maintained hilite, a tool for doing build-time syntax highligting.
It is being used on the ocaml.org website, in yocaml_markdown and also in Michael's webplats (when it works...).
And it was Michael who led me to finally fixing and adding syntax highlighting support for Python and Go. Once I was on this roll, I finally took a look at fixing the long-standing bug of trying to highlight ocaml-mdx code, like the following:
# 50.123;;
# 1 "file.ml";;
In fact, this code now highlights fine, but was the source of the bug. The syntax highlighting grammer confuses the ocaml-mdx # as a toplevel directive and trouble ensues.