OCaml Weekly News
Hello
Here is the latest OCaml Weekly News, for the week of June 23 to 30, 2026.
Table of Contents
- New release of Sek
- Js_of_ocaml / Wasm_of_ocaml 6.4
- Ocsipersist 2.1.0: type-safe persistent references with Deriving
- Modular explicits in pre-OCaml 5.5
- LRgrep 0.9: Better syntax errors for OCaml and Menhir
- Static Analysis for OCaml
- new releases: Merlin 5.8 and OCaml-LSP 1.27.0 with support for OCaml 5.5
- duras 2.1.1 — daily notes as plain text files
- Software Engineer at LexiFi (Paris)
- Owebview : OCaml binding to the webview library
- OCaml Runtime Meeting: Mon, July 6 @ 10:00 UTC (10:00 London/Cambridge, 11:00 Paris, 7pm Sydney)
- Old CWN
New release of Sek
François Pottier announced
We are pleased to announce a new release of Sek, a library that offers efficient ephemeral (mutable) and persistent (immutable) sequence data structures, plus efficient conversions between these two forms.
This new release introduces optimizations that speed up several operations, including random access (get, set), splitting, and concatenation. For details, please see the change log.
opam update && opam install sek
Happy hacking, Arthur Charguéraud & François Pottier.
François Pottier later added
Let me add this link to the documentation.
Js_of_ocaml / Wasm_of_ocaml 6.4
Hhugo announced
I'm pleased to announce the joint release of js_of_ocaml and wasm_of_ocaml 6.4.0.
Js_of_ocaml is a compiler from OCaml bytecode to JavaScript. It makes it possible to run pure OCaml programs in JavaScript environments like browsers and Node.js.
Wasm_of_ocaml is a compiler from OCaml bytecode to WebAssembly. It is highly compatible with js_of_ocaml, so you can compile your programs with wasm_of_ocaml instead of js_of_ocaml and experience overall better performance.
Most significant changes since version 6.3:
Toolchain
- OCaml 5.5.0 support.
- OxCaml support.
wasm_of_ocaml
- WASI 0.1 support — target standalone WASI runtimes (wasmtime and friends), no JavaScript host required.
- Dynlink and toplevel support — the OCaml toplevel now runs on the Wasm backend.
- An alternative effects backend based on the Stack Switching proposal.
- Pure-Wasm zstd and BLAKE2b — unmarshalling compressed values and
Digest.BLAKE2{512,256,128}no longer need the JavaScript shims, so they work under WASI too. - The legacy
numlibrary now works on Wasm (thenatprimitives were no-op stubs).
Library — new web APIs
Promisemodule — type-safe bindings to JS promises, with Lwt interop (Js_of_ocaml_lwt.Promise.{to_lwt,of_lwt}) and Promise-typedDom_htmlbindings.Fetch+Abortmodules — the Fetch API with typedAbortController~/~AbortSignalcancellation.Dom_svgaligned with SVG 2, the popover API,Intl.RelativeTimeFormat, a newPerformancemodule, additionalConsolebindings, and many moreDom_htmlbindings.
Bug fixes
- A large number of bug fixes across the compiler, runtime, library bindings, and PPX.
⚠️ Breaking changes
A few changes are source-incompatible — mostly to fix incorrect bindings and behavior — and may require updating existing code. See the migration guide.
See the documentation and the full changelog.
Vincent Balat then added
We released Eliom 12.1 at the same time, bringing compatibility with both js_of_ocaml 6.4 and OCaml 5.5.
Ocsipersist 2.1.0: type-safe persistent references with Deriving
Vincent Balat announced
Ocsipersist 2.1.0 is out. Persistent references, stores and tables can now be serialised with Deriving_Json instead of Marshal: the data is human-readable in the database and stable across OCaml versions. The new API is fully backward compatible, and Ocsipersist works as a standalone library for any OCaml program (no Eliom or Ocsigen Server required).
type user = { name : string; age : int } [@@deriving json]
(* a persistent reference, stored as readable JSON *)
let visits = Ocsipersist.Ref_json.ref ~persistent:"visits" [%json: int] 0
let count_visit () =
let%lwt n = Ocsipersist.Ref_json.get visits in
Ocsipersist.Ref_json.set visits (n + 1)
Full announcement: https://ocsigen.org/blog/posts/ocsipersist-2.1.0.html
Modular explicits in pre-OCaml 5.5
oleg said
One of the notable features of the just announced OCaml 5.5 are module-dependent functions, a.k.a. modular explicits. It is a welcome addition: it lets us express higher-ranked types in the simplest way, and is particularly good for typed tagless-final code.
It should be mentioned however that modular explicits could be done before, with little or no hassle. Perhaps there is merit to remind of that old trick~– especially because it works also with statically unknown modules (which are out of scope for OCaml 5.5 modular explicits).
As the running example we re-use the pretty-printing example in the OCaml 5.5 announcement (hereafter, Ann55).
We start however with a simpler example: pretty-printing a set
generated by the Set.Make functor. The example follows the
pattern of the pp_map function from Ann55. It is simpler than
printing a map because it involves no higher-rank types.
let pp_set (type a s)
(module M: Set.S with type elt = a and type t = s)
(pp_elt:Format.formatter->a->unit) (ppf:Format.formatter) (set:s) =
if M.is_empty set then Format.fprintf ppf "ø" else
let pp_sep ppf () = Format.fprintf ppf ",@ " in
Format.fprintf ppf "@[{@ %a@ }@]"
(Format.pp_print_seq ~pp_sep pp_elt) (M.to_seq set)
It is almost literally the pp_map example from Ann55, with set substituted for map. The main difference is type annotations. This is not a bug: I insist on writing signatures or explicit type annotations for all top-level definitions (except, perhaps, the most trivial).
The annotations could be simplified if we introduce
type ('e,'s) set = (module Set.S with type elt = 'e and type t = 's)
type 'a printer = Format.formatter->'a->unit
The example then reads
let pp_set : type e s. (e,s) set -> e printer -> s printer =
fun (module M) pp_elt ppf set -> (* ... as before ... *)
The signature tells at a glance what pp_set is doing (which is one of the benefits of signatures: it is not just for, and not mainly for, the compiler.)
We can use pp_set just like pp_map was used in the Ann55 example:
module String_set = Set.Make(String)
let () =
let m = String_set.of_list ["Zero"; "Zero"; "One"; "Un"] in
let pp_str = Format.pp_print_string in
Format.printf "%a@." (pp_set (module String_set) pp_str) m
Our rendition of modular explicits extends beyond statically known modules like String_set. For example,
(* Abstract set of elements of types 'e. The implementation is abstract *)
type 'e aset = (module Set.S with type elt = 'e)
let f : unit -> int aset = fun () ->
if Random.bool () then
(module Set.Make(Int))
else
(module Set.Make(struct type t = int
let compare x y = - Int.compare x y end))
The function f randomly returns one of two distinct Set implementations (the Set.t types are not compatible). As an application, we print a list of integers as a set (automatically sorting and removing duplicates):
let print_as_set : type a. a aset -> a printer -> a list printer =
fun (module M) pp ppf lst ->
pp_set (module M) pp ppf (M.of_list lst)
let _ = Format.printf "%a@."
(print_as_set (f ()) Format.pp_print_int) [1;2;3;1]
The result is indeed either "{ 1, 2, 3 }" or "{ 3, 2, 1 }", depending on how die is cast.
Let us now tackle the pp_map example from Ann55. It is challenging because of the higher-rank type of the map type 'a Map.S.t.
It is tempting to define the module type as
type ('k,'v,'m) map =
(module Map.S with type key = 'k and type 'a t = 'm constraint 'a = 'v)
Alas, it doesn't work:
2 | (module Map.S with type key = 'k and type 'a t = 'm constraint 'a = 'v)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: Syntax error: invalid package type: parametrized types are not supported
Package types and their with constraints come with many restrictions, which
is deeply unfortunate.
We have to resort to an encoding:
module type maps = sig
include Map.S
type v
type mt
val of_mt : mt -> v t
val to_mt : v t -> mt
end
type ('k,'v,'m) map =
(module maps with type key = 'k and type v = 'v and type mt = 'm)
One should mention that this is a strictly more general type of maps: it supports specialized implementations for particular combination of keys and values (e.g., if 'v is bool, we can use Set as the underlying structure.)
The pretty-printer of maps is the same as in the Ann55, with two additions: two occurrences of M.of_t.
let pp_map : type k v m. (k,v,m) map -> k printer -> v printer -> m printer =
fun (module M) pp_key pp_v ppf set ->
if M.is_empty (M.of_mt 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 (M.of_mt set))
It applies to statically unknown modules however, unlike pp_map in Ann55
type ('k,'v) amap = (module maps with type key = 'k and type v = 'v)
let string_map : type a. unit -> (string,a) amap = fun () ->
(module struct
include Map.Make(String)
type v = a
type mt = v t
let of_mt = Fun.id
let to_mt = Fun.id
end)
let () =
let md = string_map () in
let module M = (val (md : (string,int) amap)) in
let m = M.of_list ["Zero", 0; "One", 1] in
let pp_str = Format.pp_print_int in
Format.printf "%a@."
(pp_map (module M) Format.pp_print_string pp_str) (M.to_mt m)
I should mention that the functions like to_mt come naturally in case of tagless-final interpreters: these are the observation functions. For example:
module type lc = sig
type 'a repr
val int : int -> int repr
val lam : ('a repr -> 'b repr) -> ('a -> 'b) repr
val app : ('a -> 'b) repr -> ('a repr -> 'b repr)
type obst
type obs
val observe : obst repr -> obs
end
type ('a,'obs) lc = (module (lc with type obs = 'obs and type obst = 'a))
let ex1 : type obs. (int,obs) lc -> obs = fun (module M) -> let open M in
let t1 = app (lam (fun x -> x)) (int 1)
in observe t1
In conclusion, it would be great if one day the restrictions on package types were relaxed.
Olivier Nicole asked and Samuel Vivien replied
Thanks for pointing this out. Does this mean that modular explicits, strictly speaking, bring no additional expressivity but only a simpler way to do these things? Or are there programs that could be expressed with modular explicits but not with constrained module types?
Indeed. Modular explicit does not add any expressiveness to the language (and does not impact the soundness of the type system). Every program that can be written using modular explicits could have been written with a functor encoded as a first-class module.
We presented this encoding in section 1.5 of this paper about modular explicits : https://hal.science/hal-05428136/document
LRgrep 0.9: Better syntax errors for OCaml and Menhir
Frédéric Bour announced
Hi everyone,
I've just released LRgrep 0.9, a tool to customize the error messages of Menhir-generated LR parsers by reasoning about failure paths.
The goal is to move from generic "Syntax error at line X" messages to more intuitive diagnostics that point to the actual root cause. For example, if you accidentally put a semicolon in a local-let chain:
let x = 5;
let y = 6
let z = 7
^^^
Error: Expecting 'in' to complete local-let binding.
Hint: this might be due to the semicolon line 1, character 9
Without LRgrep, OCaml typically reports only a syntax error on the subsequent let binding. The customized parser can identify that the semicolon is the likely culprit.
How you can help (feedback wanted!)
I am looking for users to help stress-test the current specifications and identify where error messages are still lacking.
- If you use OCaml: I've patched the compiler to use LRgrep. Please try it via opam:
opam switch create 5.4.1+lrgrepPlease report cases where the error messages are confusing or where you would like to see a more helpful hint. - If you use Menhir: Give LRgrep a try on your own grammars if you want to customize error messages.
Integration with Merlin is also coming soon (PR #2072).
Huge thanks to @fpottier, @jmid, Tarides, and Jane Street for their support.
Happy testing!
Static Analysis for OCaml
fantazio announced
Hi all,
I wrote a short (and slightly opinionated) piece on the state of static analysis tooling for OCaml. The goal is to provide an overview of available tools, works in progress, and missing pieces. Let me know if there is a static analysis tool you use that is not mentioned, or if you wish one existed.
Thanks!
new releases: Merlin 5.8 and OCaml-LSP 1.27.0 with support for OCaml 5.5
vds announced
I am glad to announce new releases of Merlin and OCaml-LSP 🧙 !
Merlin is an editor service that provides modern IDE features for OCaml and OCaml-LSP is a frontend for Merlin that speaks with LSP clients.
There are a few bug fixes in these but the main feature is the stable support for OCaml 5.5.
Full changelogs:
duras 2.1.1 — daily notes as plain text files
Sergiy Duras announced
duras is a command-line tool for daily notes stored as plain text files (YYYY/MM/YYYY-MM-DD.dn). No database, no daemon, no background services.
v2.1.1 adds duras build, which converts a note collection to a static HTML website. 7,300 notes in \~220ms, single-threaded, no caching. Notes use sigline format — a plain-text structured notation where the leading character of each line declares its type.
- Source: https://codeberg.org/duras/duras
- Install:
opam install duras - Man page:
man duras
Software Engineer at LexiFi (Paris)
Nicolas Ojeda Bar announced
Dear all,
LexiFi is hiring! We are looking for a full-time Software Engineer to join our core development team.
We are particularly interested in candidates with a strong programming background, for example through previous industrial experience, contributions to open-source projects, or substantial personal projects.
LexiFi has been using OCaml for more than 25 years—we were the first software company to build our products on OCaml—and we still implement the vast majority of our stack in OCaml. If you're excited about using OCaml to solve real-world industrial problems, we would love to hear from you.
https://www.lexifi.com/careers/software_engineer/
Work at LexiFi spans a wide range of projects across multiple domains. We also remain actively engaged with the OCaml community and strive to give back in various ways, including funding community projects, helping maintain the OCaml compiler and other open-source projects, and participating in conferences.
If you have any questions, please don't hesitate to reach out to me, either here or privately.
Cheers, Nicolas
Owebview : OCaml binding to the webview library
Korkorran announced
Hi everyone! 👋
I'd like to share a small project I've been working on: owebview, a set of OCaml bindings for webview.
What is webview?
webview is a tiny, cross-platform library for building desktop GUIs using the operating system's built-in web engine — WebKit on macOS, WebKitGTK on Linux, and WebView2 on Windows. Instead of shipping a whole browser like Electron, you reuse the system one, so your apps stay small. You create a window, point it at some HTML (or a URL), and you can call back and forth between the page's JavaScript and your host language.
The gap owebview fills
What makes webview really appealing is its ecosystem: it already has bindings in a lot of languages — Go (the reference one), Rust, Python, C#, Nim, Zig, and many more. As far as I could tell, though, there wasn't one for OCaml.
owebview is an attempt to fill that gap: a thin binding that lets you drive webview directly from OCaml, with a native bridge between the page's JavaScript and your OCaml functions.
What it looks like
A complete app is about ten lines:
let () =
let w = Webview.create () in
Webview.set_title w "My first owebview app";
Webview.set_size w \~width:480 \~height:320 Webview.Hint_none;
Webview.set_html w
{|<!doctype html>
<html><body style="font-family: system-ui; text-align: center">
<h1>Hello from OCaml 👋</h1>
</body></html>|};
Webview.run w;
Webview.destroy w
And you can expose OCaml functions to the page — here window.add(a, b)
returns a JavaScript Promise resolved from OCaml:
Webview.bind w "add" (fun id req ->
let result =
match Scanf.sscanf_opt req "\[%d,%d\]" (fun a b -> a + b) with
| Some n -> string_of_int n
| None -> "null"
in
Webview.return w id \~error:false \~result)
Try it
# Run the bundled example
git clone https://github.com/korkorran/owebview.git
cd owebview
dune exec examples/hellowv.exe
# Or pin it into your own project
opam pin add owebview https://github.com/korkorran/owebview.git
Then just add (libraries owebview.webview) to your dune file. The webview.h header is vendored, and the platform-specific C++ flags are detected at build time (via pkg-config on Linux), so there's nothing to wire up by hand.
Honest status & a request
This is a compact binding / starting point, not a complete library yet:
some pieces (unbind, dispatch, full binding memory management) are intentionally left out for now.
It's also developed and tested mainly on macOS. I'd love feedback from people running it on Linux distributions — does it compile, do the opam depexts resolve, does the webkit2gtk-4.1 backend behave on your distro?
Issues and PRs are very welcome.
Repository: https://github.com/korkorran/owebview
Thanks for reading — happy to hear thoughts, suggestions, and especially Linux build reports!
OCaml Runtime Meeting: Mon, July 6 @ 10:00 UTC (10:00 London/Cambridge, 11:00 Paris, 7pm Sydney)
Tim McGilchrist announced
The next OCaml Runtime Meeting is Monday July 6th (2026-07-06) at 10:00 UTC (10:00 London/Cambridge, 11:00 Paris, 7pm Sydney). The agenda notes are here https://hackmd.io/@tmcgilchrist/S1RtObvWGe email tim@tarides.com if you want a calendar invite.
Approximately every month we have an informal meeting between OCaml developers with a focus on the language runtime/garbage collector. The meetings are for sharing understanding and ideas, not for formal planning purposes. They are open to any interested developer.
Previous meeting notes available in https://github.com/ocaml/subsystem-meetings
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.