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 ══════════════════ Archive: 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. [Sek] [change log] François Pottier later added ──────────────────────────── Let me add this link to [the documentation]. [the documentation] Js_of_ocaml / Wasm_of_ocaml 6.4 ═══════════════════════════════ Archive: 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 `num' library now works on Wasm (the `nat' primitives were no-op stubs). [Stack Switching proposal] Library — new web APIs ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ • *`Promise' module* — type-safe bindings to JS promises, with *Lwt interop* (`Js_of_ocaml_lwt.Promise.{to_lwt,of_lwt}') and Promise-typed `Dom_html' bindings. • *`Fetch' + `Abort' modules* — the Fetch API with typed `AbortController~/~AbortSignal' cancellation. • *`Dom_svg' aligned with SVG 2*, the *popover API*, *`Intl.RelativeTimeFormat'*, a new *`Performance' module*, additional `Console' bindings, and many more `Dom_html' bindings. 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]. [migration guide] [documentation] [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 ════════════════════════════════════════════════════════════════ Archive: 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: [Ocsipersist] Modular explicits in pre-OCaml 5.5 ══════════════════════════════════ Archive: 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 : LRgrep 0.9: Better syntax errors for OCaml and Menhir ═════════════════════════════════════════════════════ Archive: 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. 1. *If you use OCaml:* I've patched the compiler to use LRgrep. Please try it via opam: `opam switch create 5.4.1+lrgrep' *Please report cases where the error messages are confusing or where you would like to see a more helpful hint.* 2. *If you use Menhir:* Give LRgrep a try on your own grammars if you want to customize error messages. ‣ [General Examples] ‣ [OCaml Error Specifications] Integration with Merlin is also coming soon ([PR #2072]). Huge thanks to @fpottier, @jmid, Tarides, and Jane Street for their support. Happy testing! [General Examples] [OCaml Error Specifications] [PR #2072] Static Analysis for OCaml ═════════════════════════ Archive: 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 ════════════════════════════════════════════════════════════════════════ Archive: 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 ═════════════════════════════════════════════ Archive: 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: • Install: `opam install duras' • Man page: `man duras' [sigline] Software Engineer at LexiFi (Paris) ═══════════════════════════════════ Archive: 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. 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 [LexiFi] Owebview : OCaml binding to the webview library ═══════════════════════════════════════════════ Archive: 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]. [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. [webview] 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 │ {| │ │

Hello from OCaml 👋

│ |}; │ 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: 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) ════════════════════════════════════════════════════════════════════════════════════════════════ Archive: 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 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 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]. [Alan Schmitt] [send me a message] [the archive] [RSS feed of the archives] [caml-list] [Alan Schmitt]