A tour of the OCaml Workshop 2026
This year, for the first time, the Functional Programming Workshops were held in Paris, on Inria's, the French National Computer Science Research Institute, campus along with a watch party for the ICFP conference at Indianapolis.
As each year we attended the OCaml Workshop to see the latest advances of our favourite state-of-the-art language, and as usual it was a blast! People from various companies and labs came to present their latest work in the OCaml ecosystem: Cambridge University, Inria, LexiFi, Meta, Robur, Sorbonne University and Tarides. Here's a quick summary of the day's talks.
A new workflow to build unikernels in OCaml
Romain Calabiscetta
Romain presented a brand new way to build Solo5 unikernels in OCaml. It’s based on two new OCaml components: unic and mfetch.
This lets you build unikernels without vendoring and recompiling
entire dependency tree, as was the case with the usual opam-monorepo and
mirage approach. With Solo5, in most cases one actually only needs to
recompile C files with the Solo5 toolchain (and any package that depends on
them). unic helps you identify those packages and mfetch sets them up in a
dune vendored_dir so they can be rebuilt with the right toolchain for your
unikernel. The rest of your dependencies, the pure OCaml ones, can just be
installed via opam. No need for dune ports of your entire dependency tree
anymore.

On the picture above, you can see how they are used to vendor the right set of dependencies when building a Solo5 unikernel in the Makefile of Romain's immuable project. You can also take a look at the slides (and at Romain's wonderful drawings).
Compiling FFI-heavy OCaml to WebAssembly: An Experience Report on MOPSA
Reda Boudrouss
Reda reported on how they compiled MOPSA, a static analysis platform which depends quite heavily on C/C++ libraries (APRON, GMP, MPFR, LLVM/Clang), to WebAssembly.
They took a completely different approach to the usual "OCaml in the browser":
no js_of_ocaml or wasm_of_ocaml. They instead compiled the OCaml bytecode
runtime itself, along with all C/C++ dependencies, using
Emscripten. MOPSA is then
compiled to bytecode and interpreted by the wasm-compiled OCaml runtime.
This approach saved them from rewriting thousands of lines of stubs in JavaScript or WebAssembly.
This wasn't a walk in the park though: they had to fix some nasty 32-bit bugs to get it to work, and some features don't behave properly because WASM offers no control over the floating-point rounding mode. But it's there!
The result is a not-so-slow web version of MOPSA: 10x slower than native, but
much faster than their existing pure js_of_ocaml version!
This approach can easily be reused to compile any other project with a completely different set of C dependencies.

You can read the full article here if you'd like to know more, or browse the slides.
Security-team address
Edwin Török
Edwin gave us an overview of what the OCaml Security
Team did this year. They are the main point of
contact for all security issues in the OCaml world, especially for the compiler
and the ecosystem tools. They published 17 advisories in 2026, across 11
packages; 3 of them were on the OCaml runtime itself (Marshal buffer
over-read, Bigarray.reshape integer overflow, and command injection on
Windows via filename). They are all listed in the security advisories
database. A mailing
list is
also available if you want to stay up to date with security advisories. They
also released opam-audit, a tool
that audits your current switch against known CVEs. As it is an opam plugin,
you install it once and it is then available on all your switches. The Security
Team holds regular public meetings: the next one is on Tuesday, September 15th
(slides).
A Capability Type System for OCaml
Yoann Padioleau
Yoann presented their Caps library,
inspired by
eio's
capabilities, which provides fine-grained control over effectful or "dangerous"
resources such as IO, network and system calls, using only existing OCaml
language features.
The principle is that one forbids the use of effectful or dangerous functions from the standard library or from external libraries in their code, for instance via Semgrep rules, warnings or any linting system.
Any function that needs to access those resources must then be granted access explicitly:
val f : < Cap.stdout; Cap.fork; Cap.network; .. > -> int -> int
If you're unfamiliar with this syntax, the first argument's type is an object type. Just from the function's signature we know exactly which resources it can access.
Caps provides the abstract types for each resource, wrappers around the
standard library and Unix functions such as:
val fork : < Cap.fork; .. > -> unit -> int
and an entry point:
val main : (all_caps -> 'a) -> 'a
which can only be called once in a program and is the only way to access
capabilities. One then downcasts the all_caps object to pass it around, and
the typechecker will ensure no one accesses undeclared resources.
Yoann's slides are available here if you want to learn more, or you can even watch the talk.
Runtime Types at LexiFi: Experience Report
Nicolas Ojeda Bär
Nicolas presented LexiFi's compiler extension for runtime types, an extension that grants access to type-directed programming in your OCaml codebase.
They define a type for the type witness of a type 'a:
type 'a ttype
Their patched compiler interprets [%t: <type-expr>] and generates a
<type-expr> ttype type witness. This cannot be done by a simple PPX, because
PPXs don't have access to type information; instead, it is done during or after
type checking.
They also have a GADT xtype that describes a type's shape:
type 'a xtype =
| Unit : unit xtype
| Bool : bool xtype
| Char : char xtype
| Int : int xtype
| Float : float xtype
| String : string xtype
| Option : 'b ttype -> 'b option xtype
| List : 'b ttype -> 'b list xtype
...
and it can be obtained from a type witness with:
val xtype_of_ttype : 'a ttype -> 'a xtype
With that, they can easily generate generic printers, equality functions, JSON de/serializers, etc.:
val print : t:'a ttype -> 'a -> string
Such generic functions always take the type witness as an extra ~t labeled
argument. When this argument is omitted at a call site, their compiler
extension automatically adds it, with the type witness for the inferred type of
the argument, whenever possible.
This also comes with type properties [@t <property-name> = <string>] which
are embedded in the type witness so that generic functions can eventually
interpret them. This makes it possible to customize the behaviour of a generic
function for specific types, e.g. to mark that a record field should use a
different name when serialized to JSON.
Compared to the PPX [@@deriving ...] approach, this might offer lower
performance, as the functions aren't specialized for a specific type, but it
comes with much better ease of use and flexibility: once one has the type
witness for a type, they can pass it to any number of functions.
You can read more about LexiFi's runtime types here.
PPXs That Reach Their Destinations
Gabriel Radanne
Gabriel presented their proposal for exposing a low-level API allowing PPXs to transform functions into destination passing style (DPS for short).
OCaml 4.14 introduced the [@tail_mod_cons] transformation, which allows one
to mark a function so the compiler can turn a natural-looking recursive
function into a tail-recursive one, provided the recursive call is in tail
position modulo a constructor application. For instance, the following map
implementation for lists:
let[@tail_mod_cons] rec map f l =
match l with
| [] -> []
| x :: xs ->
let y = f x in
y :: map f xs
is, roughly speaking, transformed into something that could be written as the following pseudocode:
let rec map_dps f l dst idx =
match l with
| [] -> dst.idx <- []
| x :: xs ->
let y = f x in
let dst' = y ::{mutable} Hole in
dst.idx <- dst';
map_dps f xs dst' 1
Destinations can be seen as OCaml values with holes (write-once pointers) that can be filled later.
The proposal is to expose the following in the Obj module:
type 'a dest
val set_dest : 'a dest -> 'a -> unit
and a compiler-interpreted extension [%ocaml.value_with_holes ...] that would
be replaced by the value, with holes, and a tuple made of all the destinations
one needs to fill them:
type t = Foo of int * float array * string
let holed : t * (float Obj.dest * string Obj.dest) =
[%ocaml.value_with_holes Foo (21, [| [%hole] |], [%hole])]
This is not meant to be used directly, but rather through PPXs that would transform functions in contexts where the current compiler transformation cannot be applied.
You can read the full proposal here, and see how Gabriel and their team apply this to transform tail calls modulo Async/Await here.
Slipshow: chill coding with OCaml
Paul-Elliot Anglès d'Auriac
Paul-Elliot presented new features of his Slipshow project. Slipshow is a tool to craft slideless presentations: your presentation is a continuously rolling slip, into which you can integrate handwriting and animations. A console command gives you full control over your animations, hand-made or imported. You can even switch between WYSIWYG and WYGIWYS editors!
Slipshow is written 100% in OCaml, and the compiler's guarantees strengthened it, from safety to speed, making the developer experience much smoother than with the original JavaScript prototype. Paul-Elliot summed it up by saying that it's OCaml's exceptional design, editor tooling and robust ecosystem that made working on this project so chill: guided by the compiler, without fear of introducing bugs.

If you want to see it in action, take a look at Paul-Elliot's slides.
When Type Checking Goes Wrong, but Keeps Going
Xavier Van De Woestyne
Merlin is a well-known (multi-)editor helper
for OCaml. Where the compiler stops at the first error, merlin displays all
of them, even when the code block is unfinished; Xavier presented the recovery
system that makes this possible. For this, merlin uses a patched version of
the type checker. That system worked for a while, but the maintenance burden of
updating it with every compiler release is not sustainable.
With the help of the compiler team, they finally upstreamed this typing
recovery feature into the compiler
itself. The next OCaml 5.6.0 release will offer a new flag -typing-recovery
that can be used by merlin to show all the errors found.
Because the recovery mechanism works on incomplete or incorrect code, the errors that follow the first one might be inaccurate; this mode is therefore not recommended for regular users, who should rely on the regular compiler type error reporting.
This will greatly ease support for new compilers in merlin, though there are
still some patches to apply, such as their parsing recovery mechanism.
You can find Xavier's slides here.
JSON parsing in OxCaml: fast, but not too fast
Artem Pianykh
Artem presented their SIMD JSON parser implementation, written in OxCaml.
They used OxCaml (Jane Street's current fork of OCaml, which uses the flambda2 optimisation backend, developed here at OCamlPro) to provide a lightning fast JSON parsing library, inspired by C++'s simdjson.
Thanks to OxCaml features such as stack allocation, unboxed types and — of course — SIMD instruction support, plus a bit of elbow grease, they managed to write a parser up to 9 times faster than Yojson.
The library exposes two parsing interfaces: one that produces the usual
Json.t variant type, and a lower-level but much faster one.
It is still roughly 3 times slower than simdjson, but there's room for
improvement. Artem mentioned that unboxed variants could drastically improve
the Json.t parser, among other things.
If you'd like to know more, you can read Artem's detailed article or jump straight to the code.
A new implementation of Short-paths
Ulysse Gérard, Paul-Elliot Anglès d'Auriac
Ulysse presented a new design to select short paths for printing type error messages.
Type short-paths are very useful to make types easier to read and understand at
first glance. Instead of displaying Int.t, the compiler shows int; and if
you defined type foo = string * int * float, foo is selected by the
compiler. However, it is also very opinionated, as we saw clearly in the
survey/poll conducted live during the presentation. In more specific cases,
such as defining foo in a module Bar, some prefer to keep foo when
including that module, while others prefer Bar.foo to keep the information
about the origin.
Currently there are 2 ways to compute short paths: one implemented in the
compiler to display error messages with the -short-paths option, and the
other in Merlin. The compiler determines short paths with a lazy breadth-first
search in the environment until it finds an adequate candidate. It is quite
costly, but this is not an issue for the compiler, which shows 1 error at a
time. Merlin's method is a complex engine that explores the possible branches
more deeply when looking for a short path and, to gain performance, cuts some
of them when needed. It results in a much faster and more accurate short-path
selection.
Merlin's short-path detection has a quite high maintenance burden. The idea of Merlin's team is to have a new short-path mechanism in the compiler itself, one that would remain maintainable and perform well enough to display all type error messages. That new process takes advantage of the compiler to gather information at typing time and build the set of paths used in the source; that set is reused later, so printing doesn't slow down compilation. From it, a domain of discourse is built at printing time, following a given set of inclusion rules: for example, if a module is opened, all its types are included, and if a module is renamed, all the types defined by the original and by the substitution are added to the domain of discourse. To select the shortest path, they build a priority list sorted by cost (by length, with a malus for the presence of double underscores), then they canonicalise the first-level path of that list; if the shortest canonicalised path is valid in the current environment, it is selected. Otherwise, it loops back to the canonicalisation step.

They tested that algorithm on OxCaml, and their prototype showed much better accuracy in short-path selection (with respect to their requirements) than both the compiler and Merlin, but it remains slower than Merlin. There is some ongoing work to gain performance.
You can find their slides here.
Towards a Benchmarking Service for OCaml
Luis Eduardo de Souza Amorim, Tim McGilchrist
Tim presented their new benchmarking framework for OCaml compilers. It consists of a series of tools to run, orchestrate and visualize several types of benchmarks on your computer.
Historically, Sandmark was used to
benchmark OCaml compilers, but it is more focused on GC performance over a
multitude of small code pieces. In addition to Sandmark's micro-benchmarks,
other macro-benchmarks were
added. The idea is to test real-world applications, configured to have relevant
data, inspired by Java's Da Capo benchmark
suite. They selected a dozen OCaml ecosystem tools and wrote scripts to run
them on edge cases that highlight performance data. You can add your own tool
to the benchmarks if you want to track regressions. The projects are built and
run in isolation, with perf and
olly wrapping them to
collect system and OCaml runtime data respectively. You can see the results of
the macro-benchmarks for the OCaml 5.4.1 compiler, comparing the stable release
with a flambda-enabled build.

To round out this work, they want to offer these benchmarking tools as a service, triggered by a PR in CI, by regular jobs for OCaml releases and compiler options, etc.
We'd love to see Alt-Ergo added to the benchmarks, along with the possibility of running them for OxCaml with different flambda2 configurations!
You can take a look at Tim's extended slides.
First Class Docs in OCaml
Jon Ludlam
Jon proposed a reflection on how to handle the documentation of a project together with that of its dependencies.
odoc is the main tool to generate
documentation in OCaml ecosystem. It is used by external tools to build cross package documentation,
from odig that generates a full opam
switch documentation to
ocaml-docs-ci that generates the
online documentation for ocaml.org opam repository packages. It is also used
by dune with 2 targets, @doc that generates only the package documentation,
and @doc-new that generates also the documentation for package
dependencies.

dune @doc and odig still use odoc CLI in version 1, missing new
features introduced in newer versions. Looking at how odoc is used in
ocaml-docs-ci and dune @doc-new, the main difficulty is to build dependencies
documentation. As odoc uses .cmt and .cmti artefacts and
reimplements its own module system (in order to retrieve documentation
information dropped by the compiler), it needs to be able to link each .odoc
to an already present .odoc file of each dependency. Jon propose to consider
odoc no longer as an optional package for documentation but a default package
installed in each switch. There is already an opam plugin,
odd that wraps
each package install with documentation generation, allowing easy packages cross-reference.
It provides switch-wide documentation search for direct users or editor completion.
This still has quite an impact on switch size and build times (~5% at the moment) but there is ongoing work to reduce the overhead.
If you want to learn more, you can watch Jon's talk, or browse his slides.
In the end
We'd like to thank the organizers and INRIA for hosting this event here in Paris. It was a great occasion to hear the latest updates from the vibrant OCaml community in the French capital, and to meet and discuss with its members in person. We'd love to see this turn into a yearly recurring event, at least when ICFP's hosted in distant lands.
About OCamlPro:
OCamlPro is a R&D lab founded in 2011, with the mission to help industrial users benefit from experts with a state-of-the-art knowledge of programming languages theory and practice.
- We have a long history of creating open-source projects, such as the Opam package manager, the LearnOCaml web platform, and contributing to other ones, such as the Flambda optimizing compiler, or the GnuCOBOL compiler.
- We are also experts of Formal Methods, developing tools such as our SMT Solver Alt-Ergo (check our Alt-Ergo Users' Club) and using them to prove safety or security properties of programs.
- Interested? Check our commercial website on Titagone. Please reach out, we'll be delighted to discuss your challenges: contact@ocamlpro.com or book a quick discussion.
Most Recent Articles
2025
2024
- opam 2.3.0 release!
- Optimisation de Geneweb, 1er logiciel français de Généalogie depuis près de 30 ans
- Alt-Ergo 2.6 is Out!
- Flambda2 Ep. 3: Speculative Inlining
- opam 2.2.0 release!
- Flambda2 Ep. 2: Loopifying Tail-Recursive Functions
- Fixing and Optimizing the GnuCOBOL Preprocessor
- OCaml Backtraces on Uncaught Exceptions
- Opam 102: Pinning Packages
- Flambda2 Ep. 1: Foundational Design Decisions
- Behind the Scenes of the OCaml Optimising Compiler Flambda2: Introduction and Roadmap
- Lean 4: When Sound Programs become a Choice
- Opam 101: The First Steps