Tarides Logo
A high-contrast, zoomed out image of nine camels with riders silouetted against a sunset-coloured sky.

OCaml 5.5 Summary: What's New in the Latest OCaml Update

Posted on Tue, 23 Jun 2026

The newest version of OCaml is officially out! The latest update comes with several improvements. Of note are Relocatable OCaml, Garbage Collection Pacing changes boosting overall performance, polymorphic function parameters, and several bug fixes.

This post will take you through some of the biggest changes, so let’s get started!

Polymorphic Function Parameters

Prior to 5.5, OCaml’s functions were already polymorphic, but their parameters couldn’t be. If you passed a function as an argument to another function, the type checker would assume your function was monomorphic. You couldn’t call it at multiple different types in the same function body. With OCaml 5.5, function parameters can carry explicit polymorphic type annotations.

This is a feature upstreamed from Jane Street’s OxCaml branch of OCaml, which gives the community access to their production compiler and experimental extensions. Some of the extensions, like polymorphic parameters, get upstreamed to mainline OCaml if there is enough community support.

The example that has been used both in the 5.5 PR and in the OxCaml documentation is a good illustration. Let’s look at creation functions from ppx_typed_fields – given a record definition:

type t =
  { a : string
  ; b : int
  }

ppx_typed_fields gives you a type representing the fields indexed by their type:

type 'a field =
  | A : string field
  | B : int field
  

A useful function to provide for these types is one that can create a t if given a function that returns a value for each of the fields. This could look like:

let create f =
  { a = f A; b = f B }

But, without polymorphic parameters, the compiler would give the following error:

Line 3, characters 21-22:
3 |     { a = f A; b = f B };;
                         ^
Error: This expression has type int field
       but an expression was expected of type string field
       Type int is not compatible with type string

What has happened is that you want to apply f to two types a string field and an int field. While the compiler has inferred a monomorphic type for f int field. The function needs to be polymorphic!

From OCaml 5.5, you can annotate f as polymorphic:

let create (f : 'a. 'a field -> 'a) =
  { a = f A; b = f B }

Which will give create the following type:

val create : ('a. 'a field -> 'a) -> t

Which can be called on an appropriate function directly:

let forty_two (type a) : a field -> a = function
  | A -> "forty two"
  | B -> 42

let r = create forty_two

Thus, polymorphic parameters let you use polymorphism with minimal code, without workarounds like defining fresh types or wrapping functions in types.

GC Pacing

Another one of the major updates is a series of changes that improve the Garbage Collector's performance, making it equivalent to OCaml 4.14! This was achieved over several PRs:

  • Introduced a sweep-only phase at the start of the major GC cycle to reduce latent-garbage delay, or the time between a block becoming unreachable and it becoming available for allocation. This improves GC performance by recycling memory more quickly, often leading to reduced memory usage. This improvement was originally created for OxCaml before being upstreamed. The PR is #13580 by Stephen Dolan and Nick Barnes with review by KC Sivaramakrishnan.

  • The sweeping patch changed how the Garbage Collector tracked the blocks of free memory in the shared heap. By using a run-length encoding, the GC can efficiently skip large chunks of free memory. Consequently, the duration of the sweep is now proportional to how much of the heap is used, rather than to its entire size.

    Some micro-benchmarks show substantial speedups for sparsely populated heaps. In larger applications, the improvement is likely to be smaller.

  • Added an idle phase to the GC between Sweep and Mark. In this phase, the GC does not do any work while the mutator is allocating as usual. This has improved performance on small heaps. The work is documented in PR #14365 by Damien Doligez with review by Stephen Dolan and Nick Barnes.

Relocatable OCaml

Originally, the OCaml compiler required the Standard Library to be stored at a fixed location, which had been specified when the compiler was compiled. In other words, the install path was woven into the compiler binaries at build time.

Per the test harness PR, the RFC defines ‘relocatable’ as when: the compiler binaries are identical regardless of the installation prefix or the working directory in which the compiler was built; the compiler binaries can be used from any disk location on the system without any further alteration to the binaries; and without further alteration to the user’s shell environment.

The most noticeable impact of this change for users is that creating a new opam switch is significantly faster. Instead of waiting for opam to build OCaml from sources, the switch can simply be cloned (‘relocated’). These speed-ups also benefit Dune package management.

Furthermore, it gives OCaml developers a way to reliably distribute pre-compiled binaries, knowing that if you clone and build OCaml in two separate locations, the resulting compiler binaries will be identical.

A relocatable compiler also has ecosystem-wide benefits. It’s good for Windows support, reproducible builds, caching, and more. It also opens up possibilities for new toolchains that distribute pre-built compilers via binary packages.

The Relocatable project is spread out over three main PRs:

  • The first PR behind Relocatable is #14243, which laid the groundwork for relocation by making ld.conf path relative rather than path absolute, while also tidying up several long-standing inconsistencies in how stub libraries were discovered at runtime.

    Changes included adding support for explicit relative paths to ld.conf, which are interpreted relative to the directory the file was loaded from, and making relative paths the default; a new --with-stublibs configuration option; merging multiple ld.conf sources allowing the runtime to load and merge the file from all relevant locations rather than just the first one found; unifying the C and ocamlc implementations of the ld.conf parsing logic; and making ld.conf line-ending agnostic. The PR was created by David Allsopp and reviewed by Jonah Beckford, Damien Doligez and Hugo Heuzard.

  • Second, #14244 allowed for the absolute location of the Standard Library to be removed from both the C runtime and compiler-libs library.

    It added the new %standard_library_default primitive which lets the compiler determine the value of config.standard_library_default each time a program is linked; a new configure option --with-relative-libdir that lets the compiler expect to find the Standard Library in a location relative to where the compiler is; new --set-runtime-default command line option to allow the %standard_library_default primitive tobe overridden when linking an executable; and using options for the C compiler to make the compiler’s artefacts more reproducible. This PR was created by David Allsopp, with review by Jonah Beckford, Antonin Décimo, Damien Doligez, Samuel Hym and Vincent Laviron.

  • Thirdly, #14245 enabled bytecode executables to find and verify the correct interpreter without hardcoded paths.

    A new -launch-method command line option allowing for dynamic selection by ocamlc of either the shebang line or an executable-stub launcher for bytecode executables; new -runtime-search lets bytecode executables search for ocamlrun rather than executing it from a fixed location; a new name-mangling scheme for shared libraries and bytecode interpreter executables allowing multiple OCaml installations to safely coexist on PATH and LD_LIBRARY_PATH; and new configure options, --enable-runtime-search and --enable-runtime-search-target, which control whether the compiler's own bytecode binaries and the binaries it produces respectively can locate their runtime after relocation. The PR is by David Allsopp with review by Damien Doligez and Samuel Hym.

Standard Library Improvements

OCaml’s standard library has received several improvements with the 5.5 release, particularly for the String module, which gains a wealth of new utility functions:

Beyond strings, there are new additions across many other modules:

For numerics, Int, Int32, Int64, and Nativeint all receive floor/ceil/Euclidean division and a suite of bit-counting functions.

Concurrency sees improvements too, with Domain.count, better backtrace preservation on domain exceptions, Lazy.Mutexed for thread-safe lazy values.

Rounding things out are Seq.delay, Sys.runtime_executable, Fun.todo, and new heterogeneous-list printf functions in Format and Printf.

As you can tell, there are a lot of PRs responsible for this effort, which invariably means there are a lot of contributors! We don't have space to mention them all, but they include Daniel Bünzli, François Pottier, Gabriel Scherer, KC Sivaramakrishnan, Leonardo Santos, Jeremy Yallop, David Allsopp, Nicolás Ojeda Bär, Kate Deplaix, Sacha-Élie Ayoun, Émile Trotignon, Basile Clément, Xavier Leroy, and Pavlo Khrystenko.

… And Many More!

There are, of course, many more improvements and changes coming in OCaml 5.5 than those described above. Let’s take a quick look at a few more, and as always, check out the changelog for an exhaustive list.

  • The 5.5 update comes with a new type kind, Type_external, which can be used to distinguish external types from other types and from each other. You can now declare an abstract type with a name by using type t = external “t”. The change turns primitive types into external types, and also means that the old method of distinguishing abstract types as defined in the current module. The PR is #13712 by Takafumi Saikawa and Jacques Garrigue with review by Richard Eisenberg.
  • With a new way for C threads to register themselves to the OCaml runtime with caml_c_thread_register_in_domain, PR #14275 adds the domain’s unique ID as a parameter for where C threads can register. Before this change, all C threads would automatically be registered in domain 0. The PR is by Jack Nørskov Jørgensen with reviews from Gabriel Scherer and Guillaume Munch-Maccagnoni.
  • Replacing the winpthreads library with modern concurrency primitives using WinAPI for the MSVC and MinGW-w64 ports aims to reduce the maintenance burden and barrier to entry by making the code easier to inspect. PR #13416 removes winpthreads and implements caml_plat_* and st_* to abstract over pthreads and Windows concurrency APIs. It’s by Antonin Décimo with reviews by Samuel Hym, Gabriel Scherer, Miod Vallat, B. Szilvasy, and Nicolás Ojeda Bär.
  • Introduced generational scanning of stack frames for ARM 64-bit, POWER, and RISC-V. Generational scanning means that when stack frames are scanned, scanning stops once it reaches a frame that has not changed since the previous minor GC. Generational scanning was not included in OCaml 5 from the start since OCaml 5 originally only ran on x86, an architecture that does not support this feature. With OCaml 5’s generous stack limits, generational scanning is a very useful feature that reduces minor GC work in the presence of those deep call stacks. The PRs involved are #13574 and #13594, by KC Sivaramakrishnan and Xavier Leroy, with review by Miod Vallat, Gabriel Scherer, and Olivier Nicole; alongside multiple commits and discussions by community members elsewhere.
  • Another upstreamed feature from Jane Street’s production compiler, #14029 allowed %identity extensions to be treated as nonexpansive by the typechecker. By enabling the compiler to recognise that primitives like Obj.magic are not applications and therefore do not need to be subject to value restrictions. The original PR was opened by Stephen Dolan on the Jane Street compiler, and the upstreamed version was opened by Olivier Nicole. Reviews by Hugo Heuzard, Jacques Garrigue, Jeremy Yallop, and Gabriel Scherer.
  • Changed infix extension points and attributes, which meant that they attach to the AST node of the corresponding structure item when they appear in local modules/exceptions/opens rather than the enclosing let expression. This change makes the syntax more intuitive and is consistent with how they work for global structure items. PR #14009 by Nicolás Ojeda Bär with review by Gabriel Scherer.
  • This change extended what items are allowed in let expressions. For example, let type t = … in … is valid syntax after this update. The PR in question is #14040 opened by Nicolás Ojeda Bär with review by Valentin Gatien-Baron.

Bug Fixes

Of course, we can’t wrap things up without looking at a few bug fixes!

  • #14210: fixed a data race, identified by TSan, in the weak pointers runtime. This effort was by Gabriel Scherer and Damien Doligez, report by Olivier Nicole, and review by KC Sivaramakrishnan. Alongside #14210, several other TSan-related bug fixes were included with this release, including #14213, #14255, and #14332.

  • #13777, #14347, #14348, #14421, #14498, #14526: A series of PRs that tested that C++ compilers can link with the OCaml runtime and include its headers. Work by Antonin Décimo with reviews by David Allsopp and Gabriel Scherer.

  • #14495: Fixed an infix-tag bug in the minor collector that could result in SEGVs in multi-domain programs. The fix was by Nick Barnes with review by Gabriel Scherer.

  • #14519: Fixed a segfault that would occur when using Runtime_events with a certain set-up, due to bad error checking when calling mmap(). Fix by Mark Elvers with review by Nicolás Ojeda Bär.

  • #14644, #14647: Fixed a segfault in bytecode that would happen when the caml_make_unhandled_effect_exn exception was raised. PRs by Vincent Laviron and report by Thibaut Mattio, with reviews by Nicolás Ojeda Bär, Stephen Dolan and Olivier Nicole.

  • #14349, #14718, #14722: Another runtime fix, which addressed orphaned ephemerons in several steps. PRs by Gabriel Scherer, with reviews by Olivier Nicole and Damien Doligez, and a report by Jan Midtgaard.

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!

Open-Source Development

Tarides champions open-source development. We create and maintain key features of the OCaml language in collaboration with the OCaml community. To learn more about how you can support our open-source work, discover our page on GitHub.

Explore Commercial Opportunities

We are always happy to discuss commercial opportunities around OCaml. We provide core services, including training, tailor-made tools, and secure solutions. Tarides can help your teams realise their vision