Blog
The OCaml Planet RSS
Articles and videos contributed by both experts, companies and passionate developers from the OCaml community. From in-depth technical articles, project highlights, community news, or insights into Open Source projects, the OCaml Planet RSS feed aggregator has something for everyone.
Want your Blog Posts or Videos to Show Here?
To contribute a blog post, or add your RSS feed, check out the Contributing Guide on GitHub.
Merlin, the foundational library that underpins the reference implementation of the OCaml Language Server Protocol provides a wide range of practical features, including commands, code completion, and much more. It also implements more subtle mechanisms that are essential to delivering a smooth and responsive editing experience. Indeed, the editing environment for a programming language closely resembles the front-end of its compiler. However, whereas a compiler operates on complete, syntactically valid programs, an editor must continuously cope with programs that are incomplete, temporarily inconsistent, and often ill-typed. To handle these transient states, Merlin vendors a modified version of the OCaml compiler front-end (primarily the parser and the type checker), extending it with two recovery mechanisms: Parsing Recovery, which synthesises AST nodes to allow parsing to continue even in the presence of syntax errors. Typing Recovery, which synthesises typed AST nodes, allowing type checking to continue even when type errors are encountered. This article will focus specifically on the second mechanism, typing recovery, and how we upstreamed this feature to the mainline OCaml compiler. Typing Recovery in Action The recovery mechanisms' behaviour is easy to observe in practice. Consider, for example, the following OCaml program: let x = "ten" let y = x + 20 let z = (y + (2 * x)) && true Here, we are faced with three type errors. First, we attempt to add x and 20, which makes no sense since x has type string. Next, we attempt to multiply x (once again) by 2, which is also invalid. Finally, the expression (y + (2 * x)), which is expected to have type int, is instead used as a boolean value. When compiling this program, the OCaml compiler reports only the first type error in the compilation unit: File "lib/test.ml", line 2, characters 8-9: 2 | let y = x + 20 ^ Error: The value x has type string but an expression was expected of type int This is a very sensible design choice. Although OCaml has a highly sophisticated type system, the language's pervasive type inference means that a single mistake can easily trigger a cascade of secondary errors, obscuring the compiler's primary goal: ensuring correctness. However, applying the same strategy in a code editor raises two major (and closely related) issues: An editor that reported errors one at a time would provide a rather poor user experience. More importantly, if type checking or parsing stopped at the first syntax or type error, every feature provided by Merlin that relies on compiler structures – namely the parsetree and the typedtree – would immediately become unavailable. This would be particularly frustrating because, while actively editing a file, the program is almost always in a temporarily invalid state, whether syntactically, semantically, or both. For these reasons, the modified version of the OCaml compiler front-end embedded in Merlin always produces valid ASTs (Parsing Recovery) and typed ASTs (Typing Recovery), allowing the subsequent analysis phases to continue operating on programs that are still being written, whilst collecting errors: These two recovery mechanisms, although arguably among the most interesting features of the language server, are also one of the main reasons why upgrading Merlin to support a new OCaml release is such a challenging process. This effort is known as OCaml Release Readiness. For now, let's take a closer look at why this is the case by focussing specifically on Typing Recovery. Adapting Typing Recovery Across OCaml Versions Without going into too much detail, upgrading Merlin to a new OCaml version involves applying patches coming from two different sources: Compiler patches, corresponding to the various changes made to OCaml over time; Merlin patches, which introduce the recoveries. Because Typing Recovery is deeply intertwined with the various modules involved in OCaml's type-checking process, applying patches from two different sources (with different development cycles) makes version upgrades particularly tedious and introduces two major challenges: Each upgrade takes time, because the vendored version of the compiler front-end must be adapted while preserving all recovery information and fixing the analysis issues naturally introduced by the evolution of OCaml; Handling new recovery cases can be tedious for the people responsible for the upgrade process. Indeed, it is common to apply patches from the compiler side without having a complete understanding of their underlying rationale. For example, OCaml 5.5 recently introduced a new feature that was highly anticipated by the community: module dependent functions. This addition changes how function parameters are handled and requires careful consideration of how recovery should behave in these new situations. For these reasons, Typing Recovery could become unstable (within reasonable scenarios, of course), sometimes introducing small regressions and sometimes lacking support for newly introduced features. Reducing the Differences Between Merlin and OCaml Reducing the differences between Merlin's vendored version of the front-end and the one maintained by OCaml is therefore a crucial task. It not only helps reduce Merlin's maintenance burden, but also gives OCaml maintainers more control over how they want recovery to be handled within the type checker. Of course, this represents an additional responsibility for the maintainers (another one!), but in practice, developers introducing new features in the type checker usually have both a deep understanding of their implementation and a good intuition about how recovery should behave in the presence of errors. Integrating Typing Recovery into the Compiler At the time of writing this blog post, the Typing Recovery mechanism has been merged into the compiler. In this section, we will briefly describe its re-design and direct integration into the compiler's type checker. Typing Recovery has been part of Merlin since its very beginning. Although its implementation relies on very clever encoding, it has aged over time, and its underlying design has become increasingly difficult to understand. Integrating it into the compiler, therefore, provided an opportunity to rethink the mechanism, clean it up, and make its behaviour easier to understand. The goal was to enable a non-intrusive integration, giving maintainers the tools they need to confidently evolve and maintain it in the future. How Recovery Works, at a High Level The core of the OCaml type-checker is implemented as a set of recursive functions responsible for typing the different syntactic categories of the language, notably expressions, patterns, type declarations, modules, and classes. When a type error is encountered, these functions propagate an exception that interrupts the construction of the typing derivation. As mentioned above, the compiler adopts a first-failure reporting strategy, reporting only the first encountered error. To go beyond the first-failure reporting strategy, Merlin instruments the main recursive functions of the type-checker in order to intercept typing exceptions. When an exception is raised, it is caught so that it can be recorded (logged), and typing then resumes at the recursive call level (locally) on the other independent subterms of the program. Thus, even if the typing of a subexpression fails, independent subtrees can still be analysed, which makes it possible to report multiple typing errors within a single analysis. Resuming recursion allows new errors to be discovered, but it is not sufficient to produce an exploitable Typedtree. When a sub-derivation fails, Merlin synthesises an artificial Typedtree node whose typing judgment is that expected by the context in which it is inserted, while also preserving the fragments of the derivation that could be constructed before the typing failure, even if their attachment to the rest of the tree is no longer known. These nodes help preserve the structure of the Typedtree returned by the type-checker and maintain language server functionality despite the presence of errors. However, these generic mechanisms are not sufficient to cover all language constructs. Some phases of the type-checker perform preliminary checks before recursively typing their subterms. When these checks fail, recursion is never engaged, and no partial derivation is produced. For example, record expressions: { my_fst_field = e1; my_snd_field = e2 } The type-checker first attempts to resolve my_fst_field and my_snd_field. If, for instance, my_fst_field does not exist, it gives up without attempting to type e1 and e2. This strategy is suboptimal: subexpressions are never analysed even though they could contain useful information or independent errors. To address this issue, Merlin delays the error by assuming that the field exists. Merlin then temporarily adapts the type-checker by considering that an unknown label is not necessarily a fatal error, but may correspond to a field that will be added later. Typing then proceeds under this assumption, which makes it possible to produce partial derivations for subexpressions and to improve the quality of diagnostics. Another API for Raising Type Errors As the purpose of Typing Recovery is essentially to be managed by Merlin, we do not want to make it the default behaviour; it can therefore be enabled using a flag (-typing-recovery) passed to the compiler (which will be enabled by default by Merlin). In order to make error handling explicit, we introduce two functions intended to replace direct calls to raise in the type-checker. When the -typing-recovery flag is not enabled, these functions behave exactly like raise. In Typing Recovery mode, they record the exception in a dedicated structure, which will then be processed by the recovery handler. Typing_recovery.log_and_raise records the exception before re-raising it. It is used when processing cannot continue locally and captures the general case; Typing_recovery.log_or_raise records the exception without re-raising it when the context is able to perform local recovery, for example, by synthesising an artificial node or by continuing the typing of other subterms; it captures more fine-grained cases. This distinction makes explicit the points where recovery is possible and those where the exception must necessarily be propagated. The API guides the implementation of the type-checker and makes recovery decisions visible when adding or modifying typing rules. Today, we do not have a formal criterion to systematically determine which function to use. The integration into the OCaml type-checker was therefore guided by a large set of unit tests and empirical validation. In practice, the choice between the two functions depends on the expected behaviour of recovery, and is validated by observing the produced diagnostics. This lack of a general criterion aligns with the observations of the paper Merlin: A Language Server for OCaml (Experience Report), which notes that recovery policies would benefit from a more systematic theoretical framework. Forcing User-Facing Error Reporting In Merlin’s historical implementation, the selection of exceptions eligible to trigger a Typing Recovery scenario was mainly based on an empirical approach. The reimplementation in the compiler made it possible to identify a simple design invariant: only exceptions intended to be reported to the user (user-facing errors) should be recorded by the recovery mechanism. Conversely, internal type-checker exceptions, as well as simply re-raised exceptions (reraise), must not be captured. To introduce recovery as a systematic approach, we aim to channel user-facing exceptions so that they are always handled through a pair of functions log_and_raise and log_or_raise. To achieve this, we rely on a property of the OCaml language: exceptions are represented by the extensible type exn. This representation makes it possible to control the introduction of new exception constructors via module interfaces. By making certain exception constructors private in a module signature, it becomes possible to forbid their construction outside this module, while still allowing them to be deconstructed through pattern matching or exception handlers. This technique makes it possible to structure the flow of user-facing errors and to enforce a systematic passage through the associated logging functions. - exception Error of error + module Error : sig + type exn += private In_context of error + end = struct + type exn += In_context of error + + let log_or_raise err = + Type_recovery.log_or_raise (In_context err) + + let log_and_raise err = + Type_recovery.log_and_raise (In_context err) + end This transformation also makes it straightforward to rely on the compiler to guide the implementation by identifying and handling every possible recovery case individually and provide a precise API to handle recovery with or without resumption. Closing Words and Acknowledgements Although there are still some differences between Merlin's vendored front-end and the one maintained by OCaml, this change has already significantly reduced the number of Merlin patches that need to be applied. In the future, this will greatly simplify the process of upgrading to new OCaml versions. We recently provided a Merlin branch that anticipates the 5.6 release and relies on the new Typing Recovery mechanism! Although the next version of OCaml will expose the -typing-recovery flag to enable this mechanism, the goal is not for developers to use it directly, but rather for it to be leveraged by tools such as Merlin. Historically, Typing Recovery was designed and maintained by Merlin's two creators, Frédéric Bour and Thomas Refis. It was also maintained over the years by Ulysse Gérard. The integration into the compiler was funded by Jane Street. The review, conducted by Thomas Refis (who contributed greatly to knowledge transfer and design discussions), was funded by The OCaml Software Foundation. We would also like to thank Florian Angeletti for his quick review, guidance, and detailed explanations. This work is part of the broader effort to reduce the gap between OCaml and Merlin, and it is one of the most significant steps in that direction. We believe that the integration was carried out in a smooth and non-intrusive way, minimising the impact on maintenance work while drastically reducing the difficulty of future version upgrades. This will allow us to focus on delivering the best possible experience and continuously improve the OCaml code editing experience. Stay Tuned. Stay In Touch! Tried out the new update? Connect with us on Bluesky, Mastodon, Threads, and LinkedIn or sign up for our mailing list to stay updated on our latest projects. You can also connect with other OCaml users on Discuss. We look forward to hearing your thoughts!
Refactoring Mollymawk's internal data structures and how it persists data.
betacalendars 1.0.0 — deterministic date and calendar utilitiesSedlex 3.8.1opam 2.6.1shakuhachi v0.4.0Docfd: TUI multiline fuzzy document finder - 13.1.3 + proper user guideOUPS meetup october 2026faker 0.1.1: fake (but realistic) data, seed-compatible with faker-js
NPY to Zarr has been completed for v1 and v1.1; day10 revdeps; Tessera tools packaged and failures in bench.ci.dev.
The Foundations of Computer Science notes are now done as a Forest with live OCaml toplevels, and Part II projects sorted.
Write interfaces first, statically type check the design, then hand each module to an agent one at a time. OCaml's module language makes this a natural workflow for future formal methods too.
How to implement a VPN in OCaml
What is the OCaml ecosystem doing to make upgrading to the latest version of the language seamless? When the OCaml compiler receives a new update, it has the potential to have wide-ranging effects to the ecosystem of tools that surround it. In the past, users have hesitated to move forward with the latest release due to proper tooling support lagging behind. To improve the user experience and make transitions smoother, OCaml developers and maintainers agreed to work together to ensure that the core tools were always ready near release time. This effort is referred to as ‘OCaml Release Readiness’. It is a complex task spread across many actors in the ecosystem and supervised by Florian Angeletti (Inria) and Kate Deplaix (OCSF). In this post, we will give you a behind-the-scenes look at the work that goes into getting the OCaml ecosystem ‘release ready’. What are the ‘Core Tools’? There is no definitive rule for which tools are part of the release readiness effort, but, as a general guideline, it includes the kind of tools that provide key support in projects. If an update is incompatible with a core tool, it will be too disruptive for the user to migrate to the latest compiler version. Let’s look at the tools that are currently part of the release readiness cycle and what the maintainers do to get them up-to-date with new releases. Ppxlib Ppxlib is a library for building pre-processors in OCaml. Pre-processors are called at compile time and alter the program before compilation. They can perform a whole host of helpful functions, such as including a file, generating boilerplate, or extending the language. More or less half of the OCaml ecosystem depends (transitively) on Ppxlib. Internally, ppxlib has a copy of every abstract syntax tree (AST) of every supported compiler. Currently, that is every compiler version from 4.08 to 5.5. This is necessary because ppxlib has to migrate the ASTs of every user to a common AST (5.2 at the time of writing) to perform the pre-processing, and then migrate back to the AST of the user for the rest of the compilation process. For each new OCaml release, a new AST corresponding to the latest version is added to the migration chain, and transformations are written to and from that AST. If this AST includes new syntactic features or new representations of existing features, then the complexity of the task grows. The maintainers need to write encodings that preserve the new features and representations, particularly in the migrations to and from older ASTs. New Ppxlib releases are prepared by Nathan Rebours, via the OCaml Software Foundation (OCSF) and OCamlPro, and by Patrick Ferris at Tarides. Merlin Merlin is an editor service for OCaml that supports advanced IDE features. It is an analysis tool that provides semantic information about OCaml code, communicated to editors via a protocol such as LSP. A cornerstone of its architecture is the ability to recover from parsing and typing errors, so it can keep providing useful information on lines below the error rather than stopping. Achieving this requires patching large parts of the compiler's parser and typechecker in non-trivial ways, which makes upgrading Merlin's vendored parser and typer to each new compiler version a complex, time-consuming task. Several ongoing upstreaming efforts should make future updates much easier, like type-recovery which is scheduled to be upstreamed in the 5.6 version of the compiler, and the open proposal to upstream Merlin's new -short-paths implementation directly into the compiler as well, removing a long-standing source of drift between the two. The release readiness work for Merlin is undertaken by a team at Tarides. OCaml-LSP OCaml-LSP is the LSP frontend for Merlin. It has only minor dependencies on the compiler, so once Merlin's upgrade to a new OCaml version is done, upgrading OCaml-LSP is usually straightforward. The release readiness work for OCaml-LSP is done at Tarides. Dune Dune is a composable build system for OCaml. Its support for both monorepos and multi-package repositories has made it popular among OCaml developers. Its main branch tracks the latest compiler versions, and because it sits so early in the dependency graph, failures are quick to surface. In practice, the problems that come up are about compiling Dune itself with a newer compiler and not about the projects it builds. Strictly speaking this isn't even required, since a Dune binary built with an older compiler can still build projects with a newer one. But opam constrains a switch to a single compiler version for all packages in it, so in practice Dune has to stay compatible with the latest compiler releases. The release readiness work for Dune is done by a team at Tarides. OCamlFormat OCamlFormat parses and pretty-prints OCaml code. We need to update it every time new syntax is added to the language, which surprisingly happens in almost every OCaml minor release. OCamlFormat vendors two versions of the OCaml parser, so upgrading requires backporting patches with Git and resolving conflicts. We then update and test the printing code. The first parser is used purely for verification: OCamlformat parses the code before and after formatting, compares the two ASTs, and refuses to format if they diverge. The second parser is extended to keep more information about the concrete syntax. For example, these two snippets would have the same AST with OCaml's parser: [ 1; 2 ] and 1 :: 2 :: []. They are encoded differently with our extended parser. The release readiness work for OCamlFormat is done at Tarides. Odoc Odoc reads compiler-produced artifacts containing the abstract syntax tree of compilation units in order to generate module documentation. Since the type of those ASTs evolves between OCaml versions, odoc defines its own internal AST to be independent of the compiler version. Odoc uses cppo to adapt how the compiler's AST is loaded into odoc's internal representation at compile time. When a new OCaml version introduces changes to the typedtree, odoc fails to compile until it's adapted. At minimum, this means updating the loading logic to handle the new types while staying compatible with older versions via cppo conditionals. Beyond that, odoc's internal AST may need to be extended to represent new features, and add the logic to support rendering the changes in the generated API documentation when applicable. The release readiness work on odoc is done at Tarides. Stay in Touch Did you know how much work goes on behind-the-scenes to keep the OCaml ecosystem’s tools up-to-date with new compiler versions? Connect with us on Bluesky, Mastodon, Threads, and LinkedIn or sign up for our mailing list to stay updated on our latest projects. We look forward to hearing from you!
tinybox: 150 classic games (Pong to Quake) and Apps, in OCaml, and moreIX, a full OS (kernel, toolchain, vcs, ...) in OCamlMichel Mauny, 1959-2026dream-html 4.0.0hegel-ocaml 0.25.0 - concurrent stateful property-based testingTutorial on GADTsOCaml Compiler Implemented in C++: It's Not Just Faster—It Also Increases TrustConf.funcp.org is a scam OCaml conferenceVaast 0.0.0 + Design discussiona2a SDK in OCaml




