[Caml-list] Attn: Development Editor, Latest OCaml Weekly News

Alan Schmitt
2025-11-25T13:49:34Z

[-- Attachment #1: Type: text/plain, Size: 24256 bytes --]

Hello

Here is the latest OCaml Weekly News, for the week of November 18 to 25,
2025.

Table of Contents
─────────────────

Tapak: Experimental Web Framework Based on EIO
FUN OCaml is live on YouTube and Twitch
Native debugging on macOS (DWARF support)
Package Hygiene in Alice
crypt 2.0 - unix crypt function
opam 2.5.0~rc1 and opam-publish 2.7.1
ocaml-protoc 4.0, pbrt 4.0
Cmdliner 2.1.0 – The powershell edition
Other OCaml News
Old CWN

Tapak: Experimental Web Framework Based on EIO
══════════════════════════════════════════════

Archive:
<https://discuss.ocaml.org/t/ann-tapak-experimental-web-framework-based-on-eio/17507/1>

Syaiful Bahri announced
───────────────────────

Hello! I am a newcomer to the OCaml community, and this is my first
post.

I would like to share an experimental web framework I have been
working on, based on Eio. I am a happy user of Dream and Opium, but I
felt there was still room for improvement.

I’ve shared my motivation here:
<https://www.sbahri.com/projects/tapak/>

I would love to hear your suggestions and opinions regarding the
API. What important features do you feel are missing in existing
libraries? Personally, I miss the type-safe routing that Servant
offers, as well as the Phoenix real-time API.

FUN OCaml is live on YouTube and Twitch
═══════════════════════════════════════

Archive:
<https://discuss.ocaml.org/t/fun-ocaml-is-live-on-youtube-and-twitch/17254/3>

Continuing this thread, Sabine Schmaltz announced
─────────────────────────────────────────────────

After hiding the original live stream recording, and starting to
publish the talk recordings individually on the YouTube channel at
<https://www.youtube.com/@FUNOCaml>, we're almost done listing all the
talks!

<https://youtu.be/0Hwd7NxQ8_c?si=PkrJtJXPYS1kSAyg>

Native debugging on macOS (DWARF support)
═════════════════════════════════════════

Archive:
<https://discuss.ocaml.org/t/native-debugging-on-macos-dwarf-support/17509/1>

Joel Reymont announced
──────────────────────

With DWARF v5 support, the compiler now includes proper debug info —
line mappings, symbols, and variable names — so LLDB can actually
follow your code.

A small LLDB Python plug-in reads OCaml values at runtime and prints
them in a readable form: lists, tuples, closures, strings, etc. It
follows DWARF location lists to track where each variable lives and
uses the runtime headers to decode them. The `p' and `ocaml_vars'
commands work like normal LLDB commands but understand OCaml values.

It’s not complete yet (records and variants still show as tuples), but
it makes debugging native OCaml code straightforward. You can finally
set breakpoints by source, inspect locals, and understand what’s in
memory without switching to disassembly.

<https://joel.id/native-binary-debugging-for-ocaml/>

Package Hygiene in Alice
════════════════════════

Archive:
<https://discuss.ocaml.org/t/package-hygiene-in-alice/17513/1>

Steve Sherratt announced
────────────────────────

<https://www.alicecaml.org/blog/package-hygiene-in-alice/>

Alice is an OCaml build system and package manager I'm developing in
my spare time. This post is about Alice's packaging protocol and how
it enforces hygiene while building packages.

crypt 2.0 - unix crypt function
═══════════════════════════════

Archive:
<https://discuss.ocaml.org/t/ann-crypt-2-0-unix-crypt-function/17485/2>

Continuing this thread, Mikhail announced
─────────────────────────────────────────

Minor update: crypt 2.1.

We have added a mutex for the POSIX crypt function and a
platform-dependent implementation of crypt, i.e., crypt_r for Linux
and FreeBSD. We have also added the Ffi module for accessing native
platform bindings.

opam 2.5.0~rc1 and opam-publish 2.7.1
═════════════════════════════════════

Archive:
<https://discuss.ocaml.org/t/ann-opam-2-5-0-rc1-opam-publish-2-7-1/17515/1>

Kate announced
──────────────

Hi everyone,

We are happy to announce the first release candidate of opam `2.5.0',
which boasts a grand total of zero (0) changes compared to
`2.5.0~beta1'!

We also take this opportunity to announce the release of opam-publish
`2.7.1', whose full release notes can be seen [here].

[here] <https://github.com/ocaml-opam/opam-publish/releases/tag/2.7.1>

Changes in opam-publish 2.7.1
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

In `2.7.0', opam-publish changed the way user's branches are pushed to
their GitHub forks before opening a PR, switching from using SSH keys
to using the GitHub API token that opam-publish already requires.

`2.7.1' fixes a couple of bugs related to that where opam-publish
stopped working if the GitHub Action workflow files of upstream
opam-repository are changed, owing to the way GitHub token permissions
work. *Thanks to [@filipeom] both for the original contribution in
`2.7.0' and for subsequent work on it in `2.7.1'*.

Read our [blog post] for more details.

Please report any issues to [the opam bug-tracker] or [the
opam-publish bug-tracker].

[@filipeom] <https://github.com/filipeom>

[blog post] <https://opam.ocaml.org/blog/opam-2-5-0-rc1-publish/>

[the opam bug-tracker] <https://github.com/ocaml/opam/issues>

[the opam-publish bug-tracker]
<https://github.com/ocaml/opam-publish/issues>

Try the new opam 2.5.0 release candidate:
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

The upgrade instructions are unchanged:

For Unix systems
┌────
│ bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh) --version 2.5.0~rc1"
└────
or from PowerShell for Windows systems
┌────
│ Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) } -Version 2.5.0~rc1"
└────
Please report any issues to the [bug-tracker].

Happy hacking, <> <> The opam team <> <> :camel:

[bug-tracker] <https://github.com/ocaml/opam/issues>

ocaml-protoc 4.0, pbrt 4.0
══════════════════════════

Archive:
<https://discuss.ocaml.org/t/ann-ocaml-protoc-4-0-pbrt-4-0/17524/1>

Simon Cruanes announced
───────────────────────

Dear caml riders, I'm happy to announce the release of [ocaml-protoc]
4.0, alongside its runtime libraries `pbrt', `pbrt_yojson', and
`pbrt_services'.

Ocaml-protoc is a pure OCaml implementation of a [protobuf] compiler
and runtime. [Protobuf] is a binary serialization format and IDL
(interface description language) first introduced by Google in the
early 00s, and still in pervasive use there and elsewhere. It is
faster (to encode/decode) and more compact than JSON, and is designed
for backward compatibility on the wire.

Anyway, this new major release is *breaking* because it finally
follows the standard's semantics about [_field presence_]. By default,
many fields in protobuf are somewhat optional[^1] and are not
serialized at all if not present. Ocaml-protoc now tracks this for
every non-required field, even scalars, moving towards better
compliance with the standard. It does that either via `option' or via
a presence bitfield. Because of this, the generated code has changed
significantly and looks more like what the official `protoc' produces.

In a nutshell, each protobuf message now becomes a private record type
with mutable fields and a presence bitfield. All modification and
creation for a type `foo' is done via `make_foo' and `foo_set_<field>'
functions; presence can be checked with `foo_has_<field>'. This means
the runtime knows which fields have been explicitly modified.

I don't think ocaml-protoc is 100% compliant with the fine print on
default values in proto3, etc. but this is a lot closer than it used
to be. Thanks to the work of [@lupus] there's also a new option
validation layer.

[ocaml-protoc] <https://github.com/mransan/ocaml-protoc/>

[protobuf] <https://protobuf.dev>

[Protobuf] <https://en.wikipedia.org/wiki/Protocol_Buffers>

[_field presence_]
<https://protobuf.dev/programming-guides/field_presence/>

[@lupus] <https://github.com/Lupus>

detailed example
╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌

Let's look at this simple example:
┌────
│ syntax = "proto3";
│
│ message Person {
│ string name = 1;
│ sint64 age = 2;
│ }
│
│ message Store {
│ string address = 1;
│ repeated Person employees = 2;
│ repeated Person clients = 3;
│ }
│
│ message Company {
│ string name = 1;
│ repeated Store stores = 2;
│ repeated Company subsidiaries = 3;
│ }
└────

┌────
│ (* generated from "orgchart.proto", do not edit *)
│
│ (** {2 Types} *)
│
│ type person = {
│ name : string;
│ age : int64;
│ }
│
│ type store = {
│ address : string;
│ employees : person list;
│ clients : person list;
│ }
│
│ type company = {
│ name : string;
│ stores : store list;
│ subsidiaries : company list;
│ }
│
│
│ (** {2 Basic values} *)
│
│ val default_person :
│ ?name:string ->
│ ?age:int64 ->
│ unit ->
│ person
│ (** [default_person ()] is the default value for type [person] *)
│
│ val default_store :
│ ?address:string ->
│ ?employees:person list ->
│ ?clients:person list ->
│ unit ->
│ store
│ (** [default_store ()] is the default value for type [store] *)
│
│ val default_company :
│ ?name:string ->
│ ?stores:store list ->
│ ?subsidiaries:company list ->
│ unit ->
│ company
│ (** [default_company ()] is the default value for type [company] *)
│
│
│ (** {2 Formatters} *)
│
│ val pp_person : Format.formatter -> person -> unit
│ (** [pp_person v] formats v *)
│
│ val pp_store : Format.formatter -> store -> unit
│ (** [pp_store v] formats v *)
│
│ val pp_company : Format.formatter -> company -> unit
│ (** [pp_company v] formats v *)
│
│
│ (** {2 Protobuf Encoding} *)
│
│ val encode_pb_person : person -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_person v encoder] encodes [v] with the given [encoder] *)
│
│ val encode_pb_store : store -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_store v encoder] encodes [v] with the given [encoder] *)
│
│ val encode_pb_company : company -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_company v encoder] encodes [v] with the given [encoder] *)
│
│
│ (** {2 Protobuf Decoding} *)
│
│ val decode_pb_person : Pbrt.Decoder.t -> person
│ (** [decode_pb_person decoder] decodes a [person] binary value from [decoder] *)
│
│ val decode_pb_store : Pbrt.Decoder.t -> store
│ (** [decode_pb_store decoder] decodes a [store] binary value from [decoder] *)
│
│ val decode_pb_company : Pbrt.Decoder.t -> company
│ (** [decode_pb_company decoder] decodes a [company] binary value from [decoder] *)
│ ```
│
│ new code:
│
│ ```ocaml
│
│ (** Code for orgchart.proto *)
│
│ (* generated from "orgchart.proto", do not edit *)
│
│
│
│ (** {2 Types} *)
│
│ type person = private {
│ mutable _presence: Pbrt.Bitfield.t; (** presence for 2 fields *)
│ mutable name : string;
│ mutable age : int64;
│ }
│
│ type store = private {
│ mutable _presence: Pbrt.Bitfield.t; (** presence for 1 fields *)
│ mutable address : string;
│ mutable employees : person list;
│ mutable clients : person list;
│ }
│
│ type company = private {
│ mutable _presence: Pbrt.Bitfield.t; (** presence for 1 fields *)
│ mutable name : string;
│ mutable stores : store list;
│ mutable subsidiaries : company list;
│ }
│
│
│ (** {2 Basic values} *)
│
│ val default_person : unit -> person
│ (** [default_person ()] is a new empty value for type [person] *)
│
│ val default_store : unit -> store
│ (** [default_store ()] is a new empty value for type [store] *)
│
│ val default_company : unit -> company
│ (** [default_company ()] is a new empty value for type [company] *)
│
│
│ (** {2 Make functions} *)
│
│ val make_person :
│ ?name:string ->
│ ?age:int64 ->
│ unit ->
│ person
│ (** [make_person … ()] is a builder for type [person] *)
│
│ val copy_person : person -> person
│
│ val person_has_name : person -> bool
│ (** presence of field "name" in [person] *)
│
│ val person_set_name : person -> string -> unit
│ (** set field name in person *)
│
│ val person_has_age : person -> bool
│ (** presence of field "age" in [person] *)
│
│ val person_set_age : person -> int64 -> unit
│ (** set field age in person *)
│
│ val make_store :
│ ?address:string ->
│ ?employees:person list ->
│ ?clients:person list ->
│ unit ->
│ store
│ (** [make_store … ()] is a builder for type [store] *)
│
│ val copy_store : store -> store
│
│ val store_has_address : store -> bool
│ (** presence of field "address" in [store] *)
│
│ val store_set_address : store -> string -> unit
│ (** set field address in store *)
│
│ val store_set_employees : store -> person list -> unit
│ (** set field employees in store *)
│
│ val store_set_clients : store -> person list -> unit
│ (** set field clients in store *)
│
│ val make_company :
│ ?name:string ->
│ ?stores:store list ->
│ ?subsidiaries:company list ->
│ unit ->
│ company
│ (** [make_company … ()] is a builder for type [company] *)
│
│ val copy_company : company -> company
│
│ val company_has_name : company -> bool
│ (** presence of field "name" in [company] *)
│
│ val company_set_name : company -> string -> unit
│ (** set field name in company *)
│
│ val company_set_stores : company -> store list -> unit
│ (** set field stores in company *)
│
│ val company_set_subsidiaries : company -> company list -> unit
│ (** set field subsidiaries in company *)
│
│
│ (** {2 Formatters} *)
│
│ val pp_person : Format.formatter -> person -> unit
│ (** [pp_person v] formats v *)
│
│ val pp_store : Format.formatter -> store -> unit
│ (** [pp_store v] formats v *)
│
│ val pp_company : Format.formatter -> company -> unit
│ (** [pp_company v] formats v *)
│
│
│ (** {2 Protobuf Encoding} *)
│
│ val encode_pb_person : person -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_person v encoder] encodes [v] with the given [encoder] *)
│
│ val encode_pb_store : store -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_store v encoder] encodes [v] with the given [encoder] *)
│
│ val encode_pb_company : company -> Pbrt.Encoder.t -> unit
│ (** [encode_pb_company v encoder] encodes [v] with the given [encoder] *)
│
│
│ (** {2 Protobuf Decoding} *)
│
│ val decode_pb_person : Pbrt.Decoder.t -> person
│ (** [decode_pb_person decoder] decodes a [person] binary value from [decoder] *)
│
│ val decode_pb_store : Pbrt.Decoder.t -> store
│ (** [decode_pb_store decoder] decodes a [store] binary value from [decoder] *)
│
│ val decode_pb_company : Pbrt.Decoder.t -> company
│ (** [decode_pb_company decoder] decodes a [company] binary value from [decoder] *)
└────

[^1]: the precise semantics of presence are, imho, quite messy.

Cmdliner 2.1.0 – The powershell edition
═══════════════════════════════════════

Archive:
<https://discuss.ocaml.org/t/ann-cmdliner-2-1-0-the-powershell-edition/17527/1>

Brian Ward announced
────────────────────

Hello,

It's my pleasure to announce the version 2.1.0 of cmdliner

Cmdliner is an ISC-licensed library that allows the
declarative and compositional definition of command lines
with excellent support for command line interface user
conventions and standards.

This releases consolidates additional work on command line
completion. I added:

• Support for powershell auto-completion ([details]).
• Significant improvements to the `bash' completion.
• Updated [`dune' install instructions] in the cookbook.

I'd like to thank @dbuenzli for his gracious feedback on these
changes, and importantly for telling me “no” to several others
beforehand :)

For all the other details see the [release notes].

• Homepage: <https://erratique.ch/software/cmdliner>
• Docs: <https://erratique.ch/software/doc> or `odig doc cmdliner'
• Install: `opam install cmdliner' (once [the PR] is merged, may take
a few days)

[details]
<https://erratique.ch/software/cmdliner/doc/cli.html#user_pwsh>

[`dune' install instructions]
<https://erratique.ch/software/cmdliner/doc/cookbook.html#top_tool_support_with_dune_install>

[release notes]
<https://github.com/dbuenzli/cmdliner/blob/master/CHANGES.md>

[the PR] <https://github.com/ocaml/opam-repository/pull/28962>

Other OCaml News
════════════════

From the ocaml.org blog
───────────────────────

Here are links from many OCaml blogs aggregated at [the ocaml.org
blog].

• [Reading the Gas Meter]
• [Enabling SSH in Azure App Service for OCaml Containers: A
Production Journey]
• [A Vision for OCaml in the AI Era - Thibaut Mattio - FUN OCaml 2025]
• [OCaml at LexiFi - Nicolás Ojeda Bär - FUN OCaml 2025]
• [Testing a priority queue with Monolith]
• [Linux mode setting, from the comfort of OCaml]
• [OCaml on a Raspberry Pi]
• [I Can See The Pixels: Designing Cross-Stitch Patterns in OCaml -
Mindy Preston - FUN OCaml 2025]
• [Frameworks: No, Libraries: Yes. Developing a product in OCaml from
Scratch - FUN OCaml 2025]
• [Purely functional gRPC and HTTP/2 with OCaml - Adam Cholewiński -
FUN OCaml 2025]
• [Announcing Unikraft Support for MirageOS Unikernels]
• [Analyzing Programs with SMT Solvers - Tikhon Jelvis - FUN OCaml
2025]
• [Slipshow: A Full-Featured Presentation Tool in OCaml - Paul-Elliot
Anglès d'Auriac - FUN OCaml 2025]
• [On the path to the UK/India AI Summit with OpenUK and the ATI]
• [0xCaml From a System Engineer's Point of View - Dmitriy Kovalenko -
FUN OCaml 2025]
• [Performance Pitfalls: Tales From a Python/OCaml Codebase - Emma Jin
- FUN OCaml 2025]
• [Generating Static Websites the Functional Programming Way - Xavier
Van de Woestyne - FUN OCaml 2025]
• [From OCaml 4 to 5 and from Parmap to Effects: A legacy code
transition story - FUN OCaml 2025]
• [Conex, securing the opam-repository, is now in production]
• [OCaml Roundup: October 2025]

[the ocaml.org blog] <https://ocaml.org/blog/>

[Reading the Gas Meter] <https://www.tunbury.org/2025/11/23/gas-meter/>

[Enabling SSH in Azure App Service for OCaml Containers: A Production
Journey]
<https://fearful-odds.rocks/blog/azure-ssh-ocaml-containers-production>

[A Vision for OCaml in the AI Era - Thibaut Mattio - FUN OCaml 2025]
<https://www.youtube.com/watch/BAvXqd0QeVM?version=3>

[OCaml at LexiFi - Nicolás Ojeda Bär - FUN OCaml 2025]
<https://www.youtube.com/watch/_uwvra1NFJg?version=3>

[Testing a priority queue with Monolith]
<https://cambium.inria.fr/blog/testing-a-priority-queue-with-Monolith>

[Linux mode setting, from the comfort of OCaml]
<https://roscidus.com/blog/blog/2025/11/16/libdrm-ocaml/>

[OCaml on a Raspberry Pi]
<https://www.tunbury.org/2025/11/15/ocaml-raspberry-pi/>

[I Can See The Pixels: Designing Cross-Stitch Patterns in OCaml - Mindy
Preston - FUN OCaml 2025]
<https://www.youtube.com/watch/Q2-qIrYzSXw?version=3>

[Frameworks: No, Libraries: Yes. Developing a product in OCaml from
Scratch - FUN OCaml 2025]
<https://www.youtube.com/watch/0Hwd7NxQ8_c?version=3>

[Purely functional gRPC and HTTP/2 with OCaml - Adam Cholewiński - FUN
OCaml 2025] <https://www.youtube.com/watch/exR5eWSQ_8o?version=3>

[Announcing Unikraft Support for MirageOS Unikernels]
<https://tarides.com/blog/2025-11-13-announcing-unikraft-support-for-mirageos-unikernels>

[Analyzing Programs with SMT Solvers - Tikhon Jelvis - FUN OCaml 2025]
<https://www.youtube.com/watch/BL8bmtBfd7E?version=3>

[Slipshow: A Full-Featured Presentation Tool in OCaml - Paul-Elliot
Anglès d'Auriac - FUN OCaml 2025]
<https://www.youtube.com/watch/5HMpiGKHu9A?version=3>

[On the path to the UK/India AI Summit with OpenUK and the ATI]
<https://anil.recoil.org/notes/path-to-uk-india-ai-summit>

[0xCaml From a System Engineer's Point of View - Dmitriy Kovalenko - FUN
OCaml 2025] <https://www.youtube.com/watch/iQc5fHW5SZA?version=3>

[Performance Pitfalls: Tales From a Python/OCaml Codebase - Emma Jin -
FUN OCaml 2025] <https://www.youtube.com/watch/UfrryqltZUQ?version=3>

[Generating Static Websites the Functional Programming Way - Xavier Van
de Woestyne - FUN OCaml 2025]
<https://www.youtube.com/watch/rHGMD49eb_k?version=3>

[From OCaml 4 to 5 and from Parmap to Effects: A legacy code transition
story - FUN OCaml 2025]
<https://www.youtube.com/watch/zfGlQZ2pkss?version=3>

[Conex, securing the opam-repository, is now in production]
<https://hannes.robur.coop/Posts/ConexRunning>

[OCaml Roundup: October 2025]
<https://patrick.sirref.org/ocaml-roundup-october-2025/>

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] <mailto:alan.schmitt@polytechnique.org>

[the archive] <https://alan.petitepomme.net/cwn/>

[RSS feed of the archives] <https://alan.petitepomme.net/cwn/cwn.rss>

[caml-list] <https://sympa.inria.fr/sympa/info/caml-list>

[Alan Schmitt] <https://alan.petitepomme.net/>

[-- Attachment #2: Type: text/html, Size: 57643 bytes --]