Hello Here is the latest OCaml Weekly News, for the week of March 04 to 11, 2025. Table of Contents ───────────────── OCaml projects utilizing Category theory Docker base images and OCaml-CI support for OCaml < 4.08 ocamlmig, a tool to rewrite ocaml code, and complement `[@@deprecated]' Ortac 0.6.0 improve bug reporting Dune Developer Preview Updates ppxlib.0.36.0 I created an OCaml grammar for ANTLR4 (Earley parser compatible) Melange 5.0 Other OCaml News Old CWN OCaml projects utilizing Category theory ════════════════════════════════════════ Archive: Deep in this thread, Dmitrii Kovanikov announced ──────────────────────────────────────────────── I started writing [a series of articles about Pragmatic Category Theory] in OCaml, and there's a repository with examples: • If you're interested in a complete project, I made *CCL: Categorical Configuration Language* which leverages multiple Category Theory concepts: • [The Most Elegant Configuration Language by @chshersh] I'm currently working on a [GitHub TUI] project in OCaml but the usage of CT concepts is not that crazy there. For now, I only use Semigroup and Monoids (which some people don't even consider part of CT but just Abstract Algebra). [a series of articles about Pragmatic Category Theory] [The Most Elegant Configuration Language by @chshersh] [GitHub TUI] Docker base images and OCaml-CI support for OCaml < 4.08 ════════════════════════════════════════════════════════ Archive: Mark Elvers announced ───────────────────── The [opam repository archival] process has set the minimum supported OCaml version to 4.08 _for opam repository_. It logically follows that opam-repo-ci should only test against OCaml >= 4.08. The purpose of this post is to get a sense of whether the rest of the OCaml infrastructure should also adopt the same lower bound. The current lower bound is 4.02. Specific examples include OCaml-CI. Individual projects can already opt out of testing on older platforms by adding a lower bound in the opam file. Docker base images are built for all versions of OCaml used in the CI systems. These images are published weekly on Docker Hub. We know that these images are also used by the community, but the pull counter is not broken down by individual tag. Potentially only the latest OCaml versions are being used. Users impacted by packages which have been archived from opam repository can easily restore the packages by adding the archive repository to the opam switch. This only needs to be done once. Users can build their own Docker base images, but they would need to be rebuilt periodically to keep them up to date. Would removing testing on < 4.08 in OCaml CI or removing the Docker base images < 4.08 impact you? [opam repository archival] ocamlmig, a tool to rewrite ocaml code, and complement `[@@deprecated]' ═══════════════════════════════════════════════════════════════════════ Archive: v-gb announced ────────────── Hi, I released a new version of ocamlmig in opam, whose main change is to avoid reformatting everything in codebases that don't use ocamlformat. Instead, only subexpressions touched by a rewrite are reformatted. It also requalifies identifier in more places to preserve their meaning (e.g. when replacing `string_of_int' by `Int.to_string', there might be an `Int' module in scope that's not `Stdlib.Int'. In such case, ocamlmig would more often replace `string_of_int' by `Stdlib.Int.to_string'). Separately, I've thought about the recent addition of let+ operators in Cmdliner, and how one might migrate from the use of `$' to them. Concretetely, given: ┌──── │ let bistro () (`Dry_run dry_run) (`Package_names pkg_names) ... = the code │ open Cmdliner │ let term = Term.(const bistro $ Cli.setup $ Cli.dry_run $ ...) └──── you'd want to have instead: ┌──── │ open Cmdliner │ let term = │ Term.(Syntax.( │ let+ () = Cli.setup │ and+ (`Dry_run dry_run) = Cli.dry_run │ and+ (`Package_names pkg_names) = ... │ ... │ in │ the code)) └──── ocamlmig can now transform code this way, at the tip of the ocamlmig repo (not the last release). You can see it [in the second commit in this branch] (and further mechanical cleanups in the commits with "…" bubbles), but to explain a bit: ┌──── │ let bistro () (`Dry_run dry_run) (`Package_names pkg_names) ... = the code │ open Cmdliner │ let term = Term.(const bistro $ Cli.setup $ Cli.dry_run $ ...) └──── is first turned into: ┌──── │ open Cmdliner │ let term = Term.(const (fun () (`Dry_run dry_run) (`Package_names pkg_names) ... -> the code) │ $ Cli.setup $ Cli.dry_run $ ...) └──── which is then turned into the final code: ┌──── │ open Cmdliner │ let term = │ Term.(Syntax.( │ let+ () = Cli.setup │ and+ (`Dry_run dry_run) = Cli.dry_run │ and+ (`Package_names pkg_names) = ... │ ... │ in │ the code)) └──── The first step is done using `ocamlmig replace -w -e 'const [%move_def __f] /// const __f''. In short, what this does is anytime it sees `const some-identifier', it tries to inline the definition of the identifier. In details, the left side of the `///' specifies the code to search for, and the right side what to replace it with. `const ...' searches for literally `const' applied to one argument. `[%move_def __f]' is trickier: it matches identifiers that are let-bound somewhere in the current file, removes said let binding, and recursively matches the right hand side of the binding against `__f'. Variables that start with two underscores name a term for use in the replacement expression. The second step is done with: ┌──── │ ocamlmig replace -w \ │ -e 'const (fun __p1 __p2 __p3 -> __body) $ __e1 $ __e2 $ __e3 │ /// let open Syntax in let+ __p1 = __e1 and+ __p2 = __e2 and+ __p3 = __e3 in __body' └──── This is longer, but given the previous explanation, it's hopefully fairly clear what this does. The only twist is that ocamlmig generalizes this search/replace for three elements into an n-ary version (implicitly, although perhaps it should be explicit). And that's it. So this is the full command that I used: ┌──── │ ocamlmig replace -w \ │ -e 'const [%move_def __f] /// const __f' \ │ -e 'const (fun __p1 __p2 __p3 -> __body) $ __e1 $ __e2 $ __e3 │ /// let open Syntax in let+ __p1 = __e1 and+ __p2 = __e2 and+ __p3 = __e3 in __body' └──── which seems pretty reasonable considering the rewrite is somewhat sophisticated. In general, mechanizing a change can reduce the chance of accidentally modifying something, but in this specific case, ocamlmig also detects shadowing when moving code with `[%move_def]'. Shadowing would likely cause type errors or tests errors, but if it didn't, it'd be quite hard to catch during code review. Finally, if you want to try this out on your code, I'll note that `ocamlmig replace' is in flux, and that while the commands above work, obvious variations of them may not. [in the second commit in this branch] Ortac 0.6.0 improve bug reporting ═════════════════════════════════ Archive: Nicolas Osborne announced ───────────────────────── Hi everyone! We - at Tarides - are very pleased to announce the release of the Ortac-0.6.0 packages for specification-driven testing! Ortac/QCheck-STM is a test generator based on the [QCheck-STM] model-based testing framework and the [Gospel] specification language for OCaml. In addition to generating QCheck-STM tests based on the Gospel specifications, `Ortac/QCheck-STM' computes and display a bug report in case of test failure. This report contains the piece of Gospel specification that has been violated, a runnable scenario to reproduce the bug and the expected returned value (if there is enough information in the specification to compute it). This release improves the reporting in two ways. First, the way we need to formulate the description of the expected returned value has been made more flexible (and fixed). The main limitation was about functions returning a boolean. Because of the coercion mechanism, Gospel often transforms equalities involving a boolean into a double implication. For example: `b = Sequence.mem t.contents a' is transformed into `b = true <-> Sequence.mem t.contents a'. (For the curious, this is because `Sequence.mem' returns a `prop', not a `bool', and we don't have equality on `prop'). `Ortac/QCheck-STM' now explores more patterns, including the double implication one, to try to find a suitable description of the returned value to use in the bug report. Secondly, and more importantly, the Gospel specification language supports partial functions (`Sequence.hd' is *not* defined on the empty sequence for example). When we translate calls to such function to OCaml, we raise an exception when the call is out of the function's domain. Now, that exception was captured by QCheck at runtime, making the test a failure as expected. But the Ortac runtime was then stopped before being able to build and send the bug report to QCheck for display to the user. That was sad, so I've fixed it. We can now make use of Gospel partial functions when writing specifications and enjoy the bug report computed by `Ortac/QCheck-STM'! You can install Ortac/QCheck-STM via opam (we also advise installing and using Ortac/Dune): ┌──── │ $ opam install ortac-qcheck-stm ortac-dune └──── You'll find more information in [Ortac/QCheck-STM documentation] and in The [Ortac/Dune readme]. If you have any questions, please don't hesitate to ping me :-) Next release should be about making Ortac/QCheck-STM generate tests of a library in a parallel context (this is, after all, one of the *raison d'être* of the fantastic QCheck-STM test framework!). Happy testing! [QCheck-STM] [Gospel] [Ortac/QCheck-STM documentation] [Ortac/Dune readme] Dune Developer Preview Updates ══════════════════════════════ Archive: Leandro Ostera announced ──────────────────────── Hello everyone! :waving_hand: Hope you had a great end of 2024 and your 2025 is starting well too :D We've been hard at work at Tarides to improve the Dune Developer Preview, and we'd love to learn more about what your adoption hurdles have been, so here's a very short form you can fill to let us know what's up. Happy hacking! :two_hump_camel: ppxlib.0.36.0 ═════════════ Archive: Patrick Ferris announced ──────────────────────── The ppxlib team is happy to announce the release of `ppxlib.0.36.0'! A full account of the changes can be found [on the 0.36.0 release]. [on the 0.36.0 release] OCaml 5.2 Internal AST ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ The main change in this release is that the internal AST used in ppxlib is now the same as OCaml 5.2's AST. Previously it was 4.14.0. The internal AST dictates what features your ppx can and cannot generate. To avoid confusion, this does _not_ mean ppxlib only supports OCaml 5.2 and greater. Ppxlib still supports compilers starting at 4.08.0. *The bump to 5.2 has caused a lot of reverse dependencies to break* as the 5.2 AST represents functions differently ([see the Syntactic Function Arity RFC]). Many patches have already been sent to users of ppxlib in the past few months, but quite a few still remain. :warning: Ppx authors are advised to read [the wiki entry for upgrading to ppxlib.0.36.0]. :warning: Please do not hesitate to reach out if you need any help upgrading to `ppxlib.0.36.0'. [see the Syntactic Function Arity RFC] [the wiki entry for upgrading to ppxlib.0.36.0] Other Changes ╌╌╌╌╌╌╌╌╌╌╌╌╌ • Change `Location.none' to match the compiler's `Location.none' as of OCaml 4.08. • New ways to create context free rules using floating expansions – see [#560] for the details. • Add a `-raise-embedded-errors' flag to the driver. Setting this flag raises the first `ocaml.error' embedded in the final AST. • Export `Ast_pattern.fail' making it easier to write new pattern-matchers. • Improvements to `Ast_traverse.sexp_of' to be more concise. Do read the changes entry/release for all of the acknowledgments – thank you to everyone who contributed to this release of ppxlib! A special thanks from me to @NathanReb who has been a massive help getting this work over the line. Thank you to Tarides and Jane Street for funding my time on this release of ppxlib. [#560] I created an OCaml grammar for ANTLR4 (Earley parser compatible) ════════════════════════════════════════════════════════════════ Archive: ao wang announced ───────────────── Hi everyone, I’ve created an ANTLR4 grammar for OCaml that supports Earley parsing. Feel free to use it, and any feedback or contributions are welcome! GitHub Repository: Melange 5.0 ═══════════ Archive: Antonio Nuno Monteiro announced ─────────────────────────────── Dear OCaml users, I'm proud to announce the release of Melange 5.0 Melange is a backend for the OCaml compiler that emits JavaScript. This release features improvements across a few areas, mostly targeting OCaml 5.3 support and JavaScript expressivity: • OCaml version support: we’re releasing Melange 5 with full support across a few OCaml versions: 4.14, 5.1, 5.2 and the recently released 5.3 ‣ Melange uses a versioning scheme similar to Merlin’s: releases are suffixed with the OCaml version they support, e.g. 5.0.1-414, 5.0.1-53, etc. • We're introducing build system-aware, type-safe support for JavaScript's [dynamic import], allowing to code split Melange-generated JavaScript bundles without sacrificing type-safety. • Melange can now express [discriminated unions], a JavaScript pattern that The [release announcement] blog post covers the changes in a lot more detail. Please give it a read. I'm excited to count on the support of our financial sponsors [Ahrefs] and the [OCaml Software Foundation], without which this release would not have been possible. [dynamic import] [discriminated unions] [release announcement] [Ahrefs] [OCaml Software Foundation] Other OCaml News ════════════════ From the ocaml.org blog ─────────────────────── Here are links from many OCaml blogs aggregated at [the ocaml.org blog]. • [OpenAI and structured outputs from OCaml] • [Feature Parity Series: Statmemprof Returns!] • [Announcing Melange 5] • [Learning OCaml: Functions without Parameters] [the ocaml.org blog] [OpenAI and structured outputs from OCaml] [Feature Parity Series: Statmemprof Returns!] [Announcing Melange 5] [Learning OCaml: Functions without Parameters] 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]