OCaml Weekly News

Previous Week Up Next Week

Hello

Here is the latest OCaml Weekly News, for the week of June 16 to 23, 2026.

Table of Contents

Docfd 13.0.0: TUI multiline fuzzy document finder

Darren announced

Hi all, I am happy to announce the release of Docfd 13.0.0.

Repo - Online Demo

(Try typing /list file to get started in demo, x/ to clear search. Documentation still WIP.)

What Docfd is

Think interactive grep for text files, PDFs, DOCXs, etc, but word/token based instead of regex and line based, so you can search across lines easily.

Docfd aims to provide good UX via integration with common text editors and PDF viewers, so you can jump directly to a search result with a single key press.

Interactive use

repo.gif

Non-interactive use

repo-non-interactive.gif

Features

  • Multithreaded indexing and searching
  • Multiline fuzzy search of multiple files
  • Content view pane that shows the snippet surrounding the search result selected
  • Text editor and PDF viewer integration
  • Editable command history - rewrite/plan your actions in text editor
  • Search scope narrowing - limit scope of next search based on current search results
  • Clipboard integration

Changes since 11.0.0

Docfd has come a long way since last announcement here, I recommend checking out the changelog if you're interested in the improvements and new features.

google-drive-ocamlfuse 0.9.0

Alessandro Strada announced

Hi everyone,

I’m happy to announce that google-drive-ocamlfuse 0.9.0 is now available on opam.

This release migrates from libfuse 2 to libfuse 3, allowing the package to be installed on modern Linux distributions that no longer provide libfuse 2.

opam update
opam install google-drive-ocamlfuse

Repositories:

Best, Alessandro

OCaml 5.5.0 released

octachron announced

We have the pleasure of celebrating the birthday of Blaise Pascal by announcing the release of OCaml version 5.5.0.

Some of the highlights in OCaml 5.5.0 are:

Module-dependent Functions

Modules can now be used as function arguments in a form of lightweight functors.

For instance, we can define a function for printing a map generated by the Map.Make functor:

let pp_map (module M: Map.S) pp_key pp_v ppf set =
  if M.is_empty set then
    Format.fprintf ppf "ø"
  else
   let pp_sep ppf () = Format.fprintf ppf ",@ " in
   let pp_binding ppf (k,v) =
     Format.fprintf ppf "@[%a@ =@ %a@]" pp_key k pp_v v
   in
   Format.fprintf ppf "@[{@ %a@ }@]"
     (Format.pp_print_seq ~pp_sep pp_binding) (M.to_seq set)

We can then apply this function on a string map

module String_map = Map.Make(String)

with

let () =
  let m = String_map.of_list ["Zero", "Zero"; "One", "Un"] in
  let pp_str = Format.pp_print_string in
  Format.printf "%a@."
  (pp_map (module String_map) pp_str pp_str) m

Compared to first-class modules, the type of the function pp_map

type 'a printer = Format.formatter -> 'a -> unit
val pp_map: (module M: Map.S) -> M.key printer -> 'a printer -> 'a M.t printer

is dependent over the value of the module S, and thus the function can only applied over a statically known module:

let f (): (module Map.S) =
  if Random.bool () then
    (module Map.Make(Int))
  else
    (module Map.Make(Float))
let fail = pp_map (f ())
 Error: This expression has type
         (module M : Map.S) ->
         (Format.formatter -> M.key -> unit) ->
         (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a M.t -> unit
       but an expression was expected of type (module Map.S) -> 'b
       The module M would escape its scope
This function is module-dependent. The dependency is preserved
when the function is passed a static module argument (module M : S)
or (module M). Its argument here is not static, so the type-checker
tried instead to change the function type to be non-dependent.

Relocatable Compiler

A compiler installation can now be moved or copied with no risk of hard-to-debug errors due to mixing incompatible bytecode runtime interpreters.

In practice, this means that creating a local switch when there is a global switch with the same compiler version and configuration available can be done by cloning the global switch rather than recompiling the whole compiler.

This should considerably reduce the time required to create new local opam switches out-of-the-box.

Polymorphic Functions as Function Arguments

Higher-rank polymorphic functions can now be defined directly by using an explicit type annotation in a function argument

let apply_map (map: 'a 'b. ('a -> 'b) -> 'a list -> 'b list) =
  map string_of_int [1;2;3], map List.singleton ["x"; "y"]
let _ = apply_map List.map

Previously defining such a function required going through either a record or an object with a polymorphic field or methods

type map = { map: 'a 'b. ('a -> 'b) -> 'a list -> 'b list }
let apply_map {map} =
  map string_of_int [1;2;3], map List.singleton ["x"; "y"]

Search and Replace Substring Functions

The String module has been extended with many functions for searching and replacing substrings inside a string.

let _true = String.includes ~affix:"aba" "abbaba"
let sentence = String.replace_all ~sub:"𝄽" ~by:"word" "A 𝄽 is re𝄽ed"

The substring search is using the 2-way string matching algorithm which has the advantage of requiring constant space memory overhead independently of the needle size.

Generalised Local Definitions

It is now always possible to define locally a type, a class, a module type or any kind of item that can be defined globally:

let mandelbrot n x =
  let type t = Converge | Escape of int in
  ...
  match orbit n x with
  | Converge -> 0
  | Exit_at n -> colorize n

External Types

When interfacing with foreign function libraries, it is now possible to define external type

type int_gmp = external "mpz_t"
type float_gmp = external "mpf_t"

Compared to an abstract type definition, the external type name "mpz_t" (resp. mpf_t) makes the type distinguishable from any non-abstract types or external types with a different name.

In particular, this makes FFI types better behaved when combined with Generalised Abstract Data Types (GADTs). For instance, The typechecker is able to prove that

let ok: (int_gmp,[` A] ) Type.eq -> _ = function _ -> .

is a total function because the external type int_gmp is not compatible with a polymorphic variant type.

Warning: Abstract types in the current module

The astute reader has probably noticed in the definition above that, in OCaml 5.4.0, the typechecker does accept

type int_gmp
let ok: (int_gmp, [` A] ) Type.eq -> _ = function _ -> .

as total.

Indeed until OCaml 5.5.0, abstract types defined in the current module

type a
type b

were considered as unique and provably different

let f: 'x. (a,b) Type.eq -> 'x = function _ -> .

However, this special rule for local definition of abstract types was very brittle. As soon as one moved outside of the current module, it was no longer possible to prove that the types were different.

module M = struct
  type a
  type b
end
let fail: 'x. (M.a,M.b) Type.eq -> 'x = function _ -> .
Error: This match case could not be refuted.
       Here is an example of a value that would reach it: Equal

This special typechecking rule has been removed in OCaml 5.5.0. If you were relying on it, for instance, because you used an abstract type as type-level label in a GADTs, you can change your abstract type definition to a possibly private abbreviation of a polymorphic variant

type a = private [`A]
type b = [`B]

or a (possibly private) sum type

type a = A
type b = private B

If you were using an abstract type as both a type-level label and a FFI type, you can now use an external type definition which will give you a provably distinct type even outside of the current module.

GC improvements

Some of the ongoing work to improve the pacing of the garbage collector has been integrated in OCaml 5.5.0, two of the important changes in OCaml 5.5 GC are

  • the addition of a sweep-only phase at the start of major GC
  • the addition of an idle phase to smooth the behaviour of the GC at the start.

Many incremental changes

  • The Windows implementation is no more reliant on Winpthreads
  • Around 60 new standard library functions
  • Around 90 various improvements
  • A dozen of documentation updates
  • Around 40 bug fixes

Please report any unexpected behaviours on the OCaml issue tracker and post any questions or comments you might have on our discussion forums.

The full list of changes can be found in the full changelog.

Happy hacking, Florian Angeletti for the OCaml team.

Installation Instructions

The base compiler can be installed as an opam switch with the following commands:

opam update
opam switch create 5.5.0

The source code for the release is also directly available on:

  • Fine-Tuned Compiler Configuration

    If you want to tweak the configuration of the compiler, you can switch to the option variant with:

    opam update
    opam switch create <switch_name> ocaml-variants.5.5.0+options <option_list>
    

    where <option_list> is a space separated list of ocaml-option-* packages. For instance, for a flambda and no-flat-float-array switch:

    opam switch create 5.5.0+flambda+nffa ocaml-variants.5.5.0+options ocaml-option-flambda ocaml-option-no-flat-float-array
    

    Editor’s note: please read the post for the full changelog.

Old CWN

If you happen to miss a CWN, you can send me a message and I'll mail it to you, or go take a look at the archive or the RSS feed of the archives.

If you also wish to receive it every week by mail, you may subscribe to the caml-list.