UnifiedScript_Std and UnifiedScript_Top packages. Unified scripts are a way to write tests and documentation simultaneously. Cram tests and MDX scripts are both similar to unified scripts, but unified scripts aren’t tied to Markdown (MDX) or tied to POSIX shell commands (cram tests).
Examples:
ocaml-re converted into Markdown: ocaml-re/EXAMPLES.md. Custom OCaml REPL printers are used so that many of the tests are printed as Markdown tables. aside: Wouldn’t it be nice if tests you are already writing became documentation? That was a 4 year follow-up to What are the biggest reasons newcomers give up on OCaml? - #13 by jbeckford).ml example: The following is a snippet from a regular OCaml .ml module where the (* ... *) comments are OCaml REPL toplevel responses maintained by the unified tools.
let lyrics =
"Everybody step to the left."
(* val lyrics : string = "Everybody step to the left." *)[@ocamlformat "disable"]
let (_ : string) =
Printf.sprintf "Now let's sing: %s" lyrics
(* - : string = "Now let's sing: Everybody step to the left." *)[@ocamlformat "disable"]
Docs are at dk/docs/UNIFIED_SCRIPTS.md (of course generated by the unified tools).
Install the tools with:
opam pin add UnifiedScript_Std https://gitlab.com/dkml/build-tools/MlFront/-/releases/permalink/latest/downloads/MlFront.tar.gz
opam pin add UnifiedScript_Top https://gitlab.com/dkml/build-tools/MlFront/-/releases/permalink/latest/downloads/MlFront.tar.gz
After feedback I’ll release them officially to opam. I would like feedback overall and also specifically on the agent skill that converts expect tests to unified tests.
1 post - 1 participant
]]>A have a bunch of compiled C++ file (*.o) that use some OCaml FFI functions (__imp_caml_alloc_tuple and others). The Makefile try to package all of them in a shared object (libcuite.so).
Some OCaml functions (__imp_caml_alloc_tuple, __imp_caml_modify, __imp_caml_invalid_argument) are missing and I don’t find a library (.a file) that provides them. (Even libasmrun.a suggested by an AI).
1 post - 1 participant
]]>I wanted to share that valkey 0.2.0 is now on opam:
The main thing I wanted with this project was to focus on the current Valkey stack with the newer OCaml stack.
So valkey is built around:
A few parts I’m happy with:
Cluster support with a strong focus on durability and high availability:
topology refresh, periodic background refresh, MOVED / ASK / CLUSTERDOWN handling, replica-aware reads, AZ-aware routing, and failover-aware sharded pub/sub replay
connection/runtime behavior:
circuit breaker, reconnect handling, keepalive, TLS, and optional separation between socket I/O and parsing via Eio.Domain_manager, so one side of the system does not block the other
batch support:
scatter-gather across slots, atomic single-slot flows with WATCH / MULTI / EXEC, multi-slot helpers, and in
0.2.0 also WATCH guards for read-modify-write CAS plus cross-slot pfcount_cluster
command surface:
typed helpers across a pretty broad set of commands, Client.custom /custom_multi for custom commands and multi-node execution, and named commands so you can register command templates once and reuse them later
scripting:
local script caching and optimistic EVALSHA handling with automatic fallback/retry on NOSCRIPT, so callers don’t need to manage that flow themselves
There is also already a pretty good amount of validation around it:
integration tests, property tests, fuzzing, chaos testing, examples, and guides.
Performance-wise, it also came out pretty nicely: in several scenarios it gets to 90%+ of the C reference client.
opam update
opam install valkey eio_main
Current next steps on the roadmap are things like:
IAM + mTLSIf there’s a feature people care about, I’d be very happy to reprioritize the roadmap around real interest.
I’d also really love feedback from the OCaml community on the API, ergonomics, docs, and general design.
I’m very familiar with the Valkey world, but OCaml is more of a side fun for me than my daily language, so feedback from people who really live in the OCaml ecosystem is especially valuable to me.
If you try it, I’d love to hear what you think.
5 posts - 3 participants
]]>ocaml-wire 0.9.0 to opam!
ocaml-wire is a set of combinators to describe binary protocols in OCaml. “Binary” as in fixed-layout: sizes and shapes are mostly known up front, so you’re reading fields at known offsets rather than parsing a free-form grammar (like in Menhir). There are already many good binary parser frameworks in OCaml – here’s what’s different in ocaml-wire:
-std=c99 -Wall -Werror. So you could, in theory (I haven’t pushed on this too far for now), build and link a complex stack as an standalone, external C parser whose memory safety, single-pass behaviour, and conformance to the 3D spec are proven in F*(EverParse doesn’t give you serialisers, unfortunately).js_of_ocaml, if you need to parse the same format in the browser.A small example – a packet with two nibble-wide fields, a big-endian length, and a variable-size payload whose length is read from the Length field:
open Wire
type packet = { version : int; flags : int; length : int; payload : string }
let f_version = Field.v "Version" (bits ~width:4 U8)
let f_flags = Field.v "Flags" (bits ~width:4 U8)
let f_length = Field.v "Length" uint16be
let f_payload = Field.v "Payload" (byte_array ~size:(Field.ref f_length))
let codec =
let open Codec in
v "Packet" (fun version flags length payload -> { version; flags; length; payload })
[ f_version $ (fun p -> p.version);
f_flags $ (fun p -> p.flags);
f_length $ (fun p -> p.length);
f_payload $ (fun p -> p.payload) ]
Field.ref f_length is the dependent-size bit: the payload’s length comes from the f_length field read earlier in the same struct. That same codec value is what you hand to Everparse.schema to get the 3D file out, and what backs Codec.get / Codec.set for streaming field access from OCaml. ocaml-wire also ships the infrastructure for generating FFI stubs and for differential testing to make sure the verified C and hand-written OCaml parsers agree. So far they seem to ![]()
You can read more about it here: Thomas Gazagnaire :: Describing Binary Formats in OCaml.
To try it: opam install wire
Feedback very welcome on the issue tracker (or bellow)!
3 posts - 2 participants
]]>Many open issues describe behaviour that has already been fixed, or behaviour that is working correctly but was never verified with a test. My approach: write a cram test that demonstrates the expected behaviour, link the PR to the issue with fixes #NNNN, and let the merge close the issue automatically.
This has several advantages over simply closing an issue with a comment like “this works now”:
After a few weeks of contributing test PRs and bug fixes, I was given triager and then maintainer status on the project. This let me close issues directly when they were clearly resolved, and merge my own test PRs. Before that, the existing maintainers merged them on my behalf.
Honestly, I regret closing issues where I could have used this method but didn’t. The method is much superior to simply adding to a GitHub discussion about the bug and then closing the issue.
Here’s an accounting of what I helped close, grouped by what it took:
These issues described problems that no longer existed on main. In some cases I confirmed this myself, in others someone else had already verified it. As a triager/maintainer I was able to close them:
@all target doesn’t interact well with (include_subdirs ...) (filed 2019)DUNE_BUILD_DIR is set during test (filed 2020)executables_implicit_empty_intf expects dune >= 2.9 but was only added in 3.0 (filed 2024)These issues were resolved by writing a test that demonstrates the correct behaviour. No code changes were needed — the test itself closed the issue:
.h files copied to _build without foreign stubs (filed 2019, test: #14172)%{lib:...} in the flags stanza (filed 2020, test: #14146)These required both a test and a code change to resolve:
dune subst adds duplicate version field in opam (filed 2018, fix: #14136)root_module generates duplicate module definition (filed 2022, fix: #14135)dune runtest doesn’t execute byte-only inline tests (filed 2024, fix: #14174)dune build @ocaml-index builds too many files (filed 2025, partially fixed with #14137)(wrapped (transition ...)) broken (filed 2026, test: #14088, fix: #14090)Writing tests is a good way to learn about a code base. Each test forced me to understand how a feature actually works — not the architecture in the abstract, but the concrete behaviour in specific scenarios. The understanding compounded.
Dune’s cram tests make this easy. Each test is a self-contained shell script with expected output, checked into the repo as a .t file. The barrier to writing a test is low, which is exactly what you want when your goal is to close issues by testing them. This occasionally backfired by being “too easy” - when I wrote tests to which maintainers objected because I had not done so in a Dune-idiomatic way; but this was still net positive for sure.
Consistent presence matters. I contributed nearly every day over several weeks. I think that this helped maintainers feel that I was “present” - that efforts that they made to help me were investments; so they were less prone to holding back reviews.
1 post - 1 participant
]]>Compared to the last alpha, this new version improves the manpage for
ocamlopt and fixes:
(See the Changelog below for a full list).
Concerning the associated compiler tools, most of them are already
available (as least in a preview version), and there are patches in
progress for the remaining ones. You can track the last remaining
update efforts on the release readiness meta-issue.
Thus, it should be safe to test your libraries and programs with the
new version OCaml 5.5.0 version in preparation of the final release.
If everything goes well, we might see a release in May.
If you find any bugs, please report them to the GitHub issue tracker.
If you are interested by the full list of new features and bug fixes, the
changelog for OCaml 5.5.0 is the most up-to-date resource.
Happy hacking,
Florian Angeletti for the OCaml team.
The base compiler can be installed as an opam switch with the following commands
on opam 2.1 and later:
opam update
opam switch create 5.5.0~beta1
The source code for the beta is also available at these addresses:
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~beta1+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~beta1+flambda+nffa ocaml-variants.5.5.0~beta1+options ocaml-option-flambda ocaml-option-no-flat-float-array
All available options can be listed with opam search ocaml-option.
#14644, #14647: Fix a bug related to unhandled effects in bytecode.
(Vincent Laviron, report by Thibaut Mattio,
review by Nicolás Ojeda Bär, Stephen Dolan and Olivier Nicole)
#14349, #14718: runtime, fix in the orphaning of ephemerons
(Gabriel Scherer, review by Olivier Nicole and Damien Doligez,
report by Jan Midtgaard)
#14557, #12150, #14696: ensure that the self type of class cannot escape
through type constraints.
(Leo White, review by Florian Angeletti)
#14667: enable application related warnings for module-dependent functions
(Florian Angeletti, review by Gabriel Scherer)
#14690: Fix Name_type_mismatch error message when the expected type is an
alias: print the expanded path on the right-hand side of the equality, not
the alias twice.
(Weixie Cui, review by Florian Angeletti)
#14719, #14721: compute arity correctly for module-dependent function
(Florian Angeletti, report by Jeremy Yallop, review by Stefan Muenzel)
#14655, #14691: check for size overflow in caml_ba_reshape
(Stephen Dolan, review by Xavier Leroy)
1 post - 1 participant
]]>I’ve been working on improving Dune’s inter-library dependency tracking, and wanted to share the experience — both the technical details and what it’s like as a first-time contributor to this large open source OCaml project.
When libA depends on libB, Dune gives every module in libA a glob dependency on all .cmi files in libB. If any .cmi in libB changes, every module in libA is recompiled — even modules that never reference libB.
For projects with many libraries, this creates a cascade of unnecessary recompilations. The issue that tracks this matter #4572 has been open since 2021.
Dune already runs ocamldep to compute intra-library module dependencies. The key insight: that same output tells us which libraries each module references, via their entry module names. We can use this to filter both the build dependencies and the -I/-H compiler flags per-module.
The implementation (PR #14116 and PR #14186) works as follows:
ocamldep output (both .ml and .mli)-open flagsLib_indexLib.closure-I/-H compiler flags, partitioning into direct (visible via -I) and hidden (via -H) based on requires_compile membershipWith both deps and flags filtered, a clean build will fail if a module references a library it doesn’t declare — previously, overly-broad -I flags could mask such errors.
My first attempt (PR #14021) tried to implement the filtering in a single PR without sufficient test coverage. It was closed after review revealed that the approach was fragile in edge cases I hadn’t anticipated — particularly around transparent module aliases and virtual libraries.
Transparent module aliases. OCaml’s module alias mechanism means ocamldep doesn’t always report all libraries a module transitively depends on. If libB has module M = LibC.Something, and a module in libA uses LibB.M, ocamldep reports LibB but not LibC. The fix: transitively close the filtered library set using Lib.closure, bounded by the compilation context.
Root modules. The (root_module) stanza creates a module that implicitly aliases all libraries in the compilation context. When ocamldep reports a reference to a root module, we can’t determine which underlying libraries are actually needed, so we fall back to the full dependency set.
Virtual libraries. When virtual library implementations are present in the compilation context, parameter libraries may not appear in requires_compile, so filtering could miss them. Another fallback case.
Menhir-generated modules. These mock modules aren’t in the ocamldep dependency graph, so we skip filtering for them.
Null build overhead. The filtering reads .d files and computes library closures per-module. On a fresh dune process (no memo cache), this is new work on every build — including null builds where nothing changed. This is a real trade-off: better incremental rebuild performance at the cost of some null-build overhead.
Before the implementation PRs, I submitted six test-only PRs to document existing behavior and establish a safety net:
ocamldep behavior with transparent alias chainsThis made the implementation PRs’ diffs focused on the actual change, and gave reviewers confidence that existing behavior was preserved. It also helped me understand the edge cases that tripped up my first attempt.
The Dune maintainers (@rgrinberg and @art-w) provided thorough, constructive reviews. Some highlights:
Lib.closure from the existing library — a cleaner approach I wouldn’t have found without familiarity with Dune’s internals.ml and .mli ocamldep output need to be read, since the interface can reference different libraries than the implementation-I/-H flag filtering, which makes clean builds more precise and improves cachingThe PRs went through significant refactoring during review — the final versions are substantially tighter than the initial submissions.
Working on this was a positive experience overall, but a few things created friction:
No way to benchmark before merging. The null-build overhead question came up late in the process. I discovered through manual benchmarking that the change added ~70% to null build time — a significant regression. Dune’s benchmark CI workflow runs only on pushes to main, not on PRs. Contributor-accessible performance tooling would help catch regressions before they land.
Review momentum vs. rebasing. The test PRs merged quickly, but the implementation PR required multiple rounds of review over days. Between rounds, main moves forward, requiring rebases that risk introducing conflicts. The contributor carries the burden of keeping branches fresh. This is compounded when PRs depend on each other — every rebase of #14116 required rebasing #14186 as well. GitHub has no first-class support for PR stacks, so this is manual and error-prone. Of course, all GitHub-hosted repos suffer from this.
Flaky CI. Many CI runs had errors that were not related to my code. It was often an upstream provider of an OCaml package that was unreachable or faulty (temporarily). These problems often resolved themselves, but caused day-long delays in the PR lifetimes. The problem stems from the setup code that is run and re-run over and over in CI jobs.
The Dune codebase is well-structured, with clear separation between the build engine, rule generation, and scheduler. It is also of good quality, making it feel like time spent on keeping the quality high is worthwhile.
I found the cram test infrastructure good for testing. Each test scenario is a self-contained shell script with expected output, making it easy to document and verify exact recompilation behavior. It inspires confidence in the code.
The maintainers have been responsive and the review process, while slowed by thoroughness, is collaborative and professional. Thank you, maintainers!
9 posts - 6 participants
]]>I’ve written a tiny ppx called ppx_mixins so that one can write:
type u [@@mixins Map.OrderedType + Printable]
which gets desugared to
type u
include Map.OrderedType with type t := u
include Printable with type t := u
Not much but increases readability in a codebase that uses the “mixin” pattern a lot
One can also write
module type M = [%mixins Map.OrderedType + Printable]`
(* desugars to *)
module type M = sig
type t
include Map.OrderedType with type t := t
include Printable with type t := t
end
In particular, if you also overcome the lack of support for e.g. pretty printing (or other features) in the standard library by extending functors:
module type Hashed_and_printable = sig
include Stdlib.Hashtbl.HashedType
val pp : Format.formatter -> t -> unit
end
module Make (T: Hashed_and_printable) = struct
include Stdlib.Hashtbl.Make(T)
let pp = ...
end
You can instead write:
module Make(T: [%mixins Stdlib.Hashtbl.HashedType + Printable]) = struct
...
end
One can also override other types, e.g.
type u [@@mixins Mappable(key = int; value := v)]
(* desugars to *)
type u
include Mappable with type t := u and type key = int and type value := v
with type 'a u = 'a vwith type t = int -> boolThis is because the preprocessor parses the payload as an expression, and these don’t parse nicely a expressions.
Deeper support (e.g. for mixins with type parameters) would probably also require language support.
1 post - 1 participant
]]>We initially built a SIP server implementation in OCaml, which was a perfect fit. We needed to connect it to the rest of our system in a language agnostic way. We chose gRPC and that’s where things got difficult.
At a smaller scale our own buggy implementation of gRPC on top of h2 was good enough. Then we started scaling and at the same time transitioned to OCaml 5.0. The combination of those factors resulted in:
Long story short we ended up:
Then, in late 2025, we had to add new features to the SIP server and something inside us just broke. The SIP server had accumulated quite a bit of legacy code over 3.5 years, so we wanted to rewrite some parts. We were extremely excited to build the new architecture centered around effects — only to discover that user-space effects and concurrency libraries don’t really work well together, since callbacks lose their scope (duh). That was the final straw.
We decided to run two 1-week stints to try rewriting the service in Rust and Go. We ended up choosing Rust. We still miss OCaml, but not having to implement and maintain our own http2/grpc/sip stack is a breath of fresh air.
So here we are — we’re gradually removing OCaml from our stack, and that’s why we’d like to donate:
dialo branch, eio only at the moment, battle tested on production. It does have some bugs in it but few and it’s pretty solid.Please write dms to me and feel free to ask me or @adamchol about specifics.
3 posts - 3 participants
]]>They enabled it a few months ago already, but I forgot to post it here.
The code navigation is not always 100% accurate, but good enough to be helpful. If you encounter any issues with incorrectly highlighted code, this could be a tree-sitter-ocaml issue.
2 posts - 2 participants
]]>We’re excited to announce the relaunch of OCaml.jp (https://ocaml.jp/), the OCaml Japan User Group!
Our goal is to grow and energize the OCaml community in Japan by bringing together users to foster broader adoption and deeper engagement with OCaml across the country.
We’ve already kicked off a couple of initiatives:
OCaml Weekly News - Japanese Edition: We’re maintaining a Japanese translation of the OCaml Weekly News at https://ocaml.jp/cwn-ja/, making it easier for Japanese speakers to stay up to date with the latest happenings in the OCaml ecosystem.
OCaml Meetup in Tokyo (August 2026): We are currently preparing to host an OCaml Meetup in Tokyo this August, the first one in over ten years! More details will be shared soon; please stay tuned!
We’re using Discord as our primary communication platforms. Whether you’re based in Japan, are a Japanese-speaking OCaml enthusiast anywhere in the world, or simply want to connect with the Japanese OCaml community, we’d love to have you join us!
Please use the following link to join Discord: https://discord.gg/qQTbny8KF4
We look forward to building a vibrant OCaml community in Japan together. よろしくお願いします!
1 post - 1 participant
]]>opam 2.5.1 is now available. This release is fixing a security issue (OSEC-2026-03) and other minor things. Thanks to @andrew for reporting this issue.
We invite everyone to upgrade to 2.5.1 as soon as possible.
If you depend on the older opam package of your preferred distribution, distributions such as Debian Stable have already started backporting the relevant fix and the patched version should be available there very soon.
You can read our blog post for relevant links and details.
The upgrade instructions are unchanged:
For Unix systems
bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh) --version 2.5.1"
or from PowerShell for Windows systems
Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) } -Version 2.5.1"
Please report any issues to the bug-tracker.
Happy hacking,
<> <> The opam team <> <> ![]()
1 post - 1 participant
]]>*.ml file. Any help would be appreciated. I am using Emacs version 30.2 on Linux. I can suppress defvaralias warnings, but I’d rather not if this can be fixed somehow.
Warning:
⛔ Warning (defvaralias): Overwriting value of ‘ocaml-eglot-hook’ by aliasing to ‘ocaml-eglot-mode-hook’
Warning stack trace:
Debugger entered: nil
(progn (debug))
(if (eq new-alias 'ocaml-eglot-hook) (progn (debug)))
defvaralias@trace-defvarialias(ocaml-eglot-hook ocaml-eglot-mode-hook)
apply(defvaralias@trace-defvarialias (ocaml-eglot-hook ocaml-eglot-mode-hook))
defvaralias(ocaml-eglot-hook ocaml-eglot-mode-hook)
byte-code("\301\302N\204\f\0\303\301\302\304#\210\303\301\305\306#\210\303\301\307\310C#\210\311\312\313\10\310\211%\210\314\315\312\"\210\316\315\312\317#\210\314\320\321\"\210\316\320\312\317#\210\322\323\301\"\210\324\323\301\317#\210\322\325\300\"\210\324\325\300\317#\210\326\315!\207" [ocaml-eglot-mode-map ocaml-eglot-mode-hook variable-documentation put "Hook run after entering or leaving `ocaml-eglot-mode'.\nNo problems result if this variable is not bound.\n`add-hook' automatically binds it. (This is true for all hook variables.)" custom-type hook standard-value nil add-minor-mode ocaml-eglot-mode " OCaml-eglot" defalias ocaml-eglot make-obsolete "1.4.0" ocaml-eglot-setup ocaml-eglot--setup defvaralias ocaml-eglot-hook make-obsolete-variable ocaml-eglot-map provide] 6)
ocaml-eglot()
run-hooks(change-major-mode-after-body-hook prog-mode-hook tuareg-load-hook tuareg-mode-hook)
apply(run-hooks change-major-mode-after-body-hook (prog-mode-hook tuareg-load-hook tuareg-mode-hook))
run-mode-hooks(tuareg-mode-hook)
tuareg-mode()
set-auto-mode-0(tuareg-mode nil)
set-auto-mode--apply-alist((("dune-project\\'" . dune-mode) ("dune\\'" . dune-mode) ("\\(?:\\`\\|/\\)dune\\(?:\\.inc\\|\\-project\\|\\-workspace\\)?\\'" . dune-mode) ("\\.ocamlinit\\'" . tuareg-mode) ("[./]opam_?\\'" . tuareg-opam-mode) ("\\.mly\\'" . tuareg-menhir-mode) ("\\.eliomi?\\'" . tuareg-mode) ("\\.ml[ip]?\\'" . tuareg-mode) ("\\.mli\\'" . tuareg-mode) ("\\.ml\\'" . tuareg-mode) ("\\.desktop\\(\\.in\\)?$" . desktop-entry-mode) ("CMakeLists\\.txt\\'" . cmake-mode) ("\\.cmake\\'" . cmake-mode) (".at'" . autotest-mode) ("\\.gpg\\(~\\|\\.~[0-9]+~\\)?\\'" nil epa-file) ("\\.elc\\'" . elisp-byte-code-mode) ("\\.zst\\'" nil jka-compr) ("\\.dz\\'" nil jka-compr) ("\\.xz\\'" nil jka-compr) ("\\.lzma\\'" nil jka-compr) ("\\.lz\\'" nil jka-compr) ("\\.g?z\\'" nil jka-compr) ("\\.bz2\\'" nil jka-compr) ("\\.Z\\'" nil jka-compr) ("\\.vr[hi]?\\'" . vera-mode) ("\\(?:\\.\\(?:rbw?\\|ru\\|rake\\|thor\\|axlsx\\|jbuilder\\|rabl\\|gemspec\\|podspec\\)\\|/\\(?:Gem\\|Rake\\|Cap\\|Thor\\|Puppet\\|Berks\\|Brew\\|Fast\\|Vagrant\\|Guard\\|Pod\\)file\\)\\'" . ruby-mode) ("\\.re?st\\'" . rst-mode) ("/\\(?:Pipfile\\|\\.?flake8\\)\\'" . conf-mode) ("\\.py[iw]?\\'" . python-mode) ("\\.m\\'" . octave-maybe-mode) ("\\.less\\'" . less-css-mode) ("\\.editorconfig\\'" . editorconfig-conf-mode) ("\\.scss\\'" . scss-mode) ("\\.cs\\'" . csharp-mode) ("\\.awk\\'" . awk-mode) ("\\.\\(u?lpc\\|pike\\|pmod\\(\\.in\\)?\\)\\'" . pike-mode) ("\\.idl\\'" . idl-mode) ("\\.java\\'" . java-mode) ("\\.m\\'" . objc-mode) ("\\.ii\\'" . c++-mode) ("\\.i\\'" . c-mode) ("\\.lex\\'" . c-mode) ("\\.y\\(acc\\)?\\'" . c-mode) ("\\.h\\'" . c-or-c++-mode) ("\\.c\\'" . c-mode) ("\\.\\(CC?\\|HH?\\)\\'" . c++-mode) ("\\.[ch]\\(pp\\|xx\\|\\+\\+\\)\\'" . c++-mode) ("\\.\\(cc\\|hh\\)\\'" . c++-mode) ("\\.\\(bat\\|cmd\\)\\'" . bat-mode) ("\\.[sx]?html?\\(\\.[a-zA-Z_]+\\)?\\'" . mhtml-mode) ...) nil nil)
set-auto-mode()
normal-mode(t)
after-find-file(nil t)
find-file-noselect-1(#<buffer test.ml> "~/projects/cs3110/unit-testing/test.ml" nil nil "~/projects/cs3110/unit-testing/test.ml" (1021339 44))
find-file-noselect("~/projects/cs3110/unit-testing/test.ml" nil nil nil)
find-file("~/projects/cs3110/unit-testing/test.ml")
recentf-open-most-recent-file(2)
recentf-open-most-recent-file-2()
funcall-interactively(recentf-open-most-recent-file-2)
command-execute(recentf-open-most-recent-file-2)
My config (matches official suggestion):
Note: I’m using straight.el configured such that :ensure t isn’t necessary.
(use-package tuareg
:mode (("\\.ocamlinit\\'" . tuareg-mode)))
(use-package ocaml-eglot
:after tuareg
:hook
(tuareg-mode . ocaml-eglot)
(ocaml-eglot . eglot-ensure))
3 posts - 2 participants
]]>Stk is a SDL-based graphical user interface toolkit. Its interface is inspired by Gtk and should look familiar to developers using Lablgtk.
Stk 0.6 is available. Among changes listed here, this release introduces Tooltips and handling of user’s configuration, including user-defined themes.
The stk* packages are available from my personal opam repository.
1 post - 1 participant
]]>
It’s not in Jussieu as usual but in ENS Ulm! If you’re not familiar with the place, there is a map of the buildings.
Please, register on meetup as soon as possible to let us know how many pizza we should order.
For more details, you may check the OUPS’ website .
Moreover, we’d like to announce that the organizing team moved to the OCaml Zulip. Feel free to contact us there if you’d like to suggest talks.
This time we’ll have the following talks:
When Turing machines meet GADTs – Florian Angeletti
Abstract (click for more details)Extending OCaml’s pattern matching – Yanni Lefki
Abstract (click for more details)After the talks there will be some pizzas offered by the OCaml Software Foundation and later on we’ll move to a pub nearby as usual.
1 post - 1 participant
]]>we just finished and deployed Albatross and Mollymawk to support virtual machines that are not MirageOS unikernels.
The reasoning is simple: we embedded so many nice features (metrics, console output, deploying via web UI, multi-tenant, startup dependencies, restart-on-failure,..) that we really wanted to deploy our non-MirageOS virtual machines as well with the same mechanism and don’t have duplicated code all over.
For the time being, it only supports FreeBSD BHyve, but let us know if you’re interested in other virtualization technologies and we will prioritize that work!
We also wrote a brief blog article with screenshots: Robur's blog - Mollymawk supporting other virtual machines
12 posts - 4 participants
]]>Fair warning: this is still a public test mailing list for now. In the long run, it will focus on our ptt project.
It all started with Mr.MIME, our library for decoding and encoding emails. It’s a synthesis of the relevant RFCs, but more importantly it’s been battle-tested against real-world emails from the IEEE, Enron, KVM and, most recently, the caml-list.
This work also let us build Hamlet, a database of valid random emails generated using a fuzzer.
Under the hood, Mr.MIME relies on unstrctrd for decoding the most general form of values found in an email (with internationalisation support via rosetta) and prettym for encoding emails while respecting SMTP constraints and (Comment) Folding Whitespace handling.
Then came colombe, our OCaml implementation of the SMTP protocol. It uses ocaml-tls for STARTTLS support.
The protocol is supposedly “simple” (though the Internet always has surprises in store), but from day one we designed colombe to be independent of any scheduler and network layer. That way it slots right into unikernels without friction.
On top of these core components, we built several email security layers:
We wrote a short article about all of this here.
Our first experiments already showed that we could handle emails with MirageOS unikernels. But we also hit real limitations: memory leaks, security vulnerabilities, and build issues.
So we decided to start fresh, and take the opportunity to fully embrace OCaml 5 and effects. We rebuilt the key pieces from scratch:
ptt is built on this new stack, and so far we haven’t observed any memory leaks (thanks to mkernel-memtrace for tracing memory usage, viewable via memtrace-viewer). The CVEs related to mirage-tcpip were taken into account during mnet’s development, and the build story is much simpler now. A GitHub action can build and actually run the unikernel to test it, as you can see with mnet.
Other unikernels using this approach are available too. If you’re curious, check out this tutorial on creating a unikernel in OCaml.
ptt also tackles the deployment question. We have an article presenting the “stateless” aspect of ptt. We’d also like to (re)introduce Albatross, our secure unikernel orchestrator, and Mollymawk, a web interface for deploying unikernels (which is itself a unikernel).
More broadly, this is what our cooperative is working towards: we really want to improve the user experience, whether you’re a developer or a deployer. We believe that actually developing, deploying, and using our unikernels is the only way to get them adopted more widely. So make sure to follow us on these projects too!
Along the way, we found it really helpful to have a tool that lets you track every stage of an email’s lifecycle. That’s how blaze came about: a Swiss Army knife for handling emails.
It’s still experimental, but it already lets you:
mbox or maildirblaze is how we iterate on our library APIs and validate implementations. It’s experimental, but it’s gradually turning into a full email client.
We’d also like to present the stem project, which extracts word roots from a document (such as an email) and tokenises them to get something analysable without the complexity of natural language. This tokenisation is what powers our small bm25 search engine. You can see results here.
This is also what drives our caml-list search engine, available as a unikernel: blame, which you can try at https://caml-list.robur.coop (powered by vif).
Beyond search, there’s also email indexing via Message-IDs. For that we built bancos: a persistent radix tree in OCaml that supports parallel access! More details here.
Finally, our indexing system uses the PACKv2 format (the same one Git uses to store objects), implemented by the carton library. It has proven its stability through the ocaml-git project, so we decided to reuse it for archiving emails (much like public-inbox did, though in a different form).
Thanks to all this work, OCaml now has a solid set of email-related projects. This journey started back in 2016 and there’s still a long way to go, as we always aim to offer robu(r)st, battle-tested solutions. Unlike some implementations in other languages (though we are in discussion with folks in the Rust community), ours actually adhere to the standards!
It may not seem like a big deal, and you won’t see any major difference when just exchanging emails, but we believe this approach paves the way for a better internet. In the form of unikernels, it represents a genuine reclaiming of the means of communication!
1 post - 1 participant
]]>Looking for feedback! Many thanks

1 post - 1 participant
]]>TL;DR: Consider adding the caqti.classic to your link line (available since this version). It currently only re-exports the caqti library, but will be replaced by a compatibility layer for the Caqti 2 API in Caqti 3 to allow incremental migration.
Note that some of the new features of this release are only accessible
through the still unstable caqti.template library, which will soon be
stabilized as part of an upcoming Caqti 3 API. Due to adjustments to module
names, switching to caqti.template will not ensure future compatibility.
Instead, users are encouraged to add the new caqti.classic to their
dependencies to ease migration to Caqti 3.
Added an alias caqti.classic for the caqti library. This will be
turned into a library providing compatibility with the current Caqti 2
API when Caqti 3 is released. The two APIs can be used side-by-side to
allow incremental migration.
The dialect descriptor for MariaDB in caqti.template now provides the
server version.
Added Query.parens, Query.litf, Query.vars, Row_type.fields to
the caqti.template library.
Revised the interface for creating row types to allow instantiating
parametric types applicatively. Before this change, each application of
a function representing a parametric type would generate a new type ID
even when applied to the same type parameter argument. The key part of
this interface is the Caqti_template.Constructor_type module.
The query functions provided to request templates are now memoized, so
that they are only called once per dialect. This is meant as an
optimization; it’s still good practise to avoid side-effects here.
The new function Query.with_pos_of allows query generators to add
source locations to the syntax tree, which will be emitted as SQL
comments in the query string if the enable_query_annotations is set.
This should simplify debugging when the query is assembled from
different parts of the application code.
The still unstable caqti.template library now supports multiple
statements in a single request template, sent opportunistically as a
single query where allowed. This is still experimental.
Fixed package scoping of shim rules for sqlite3 (#133 by mefyl).
Fixed the error classification for the SQLite3 driver (#132).
Fixed reconnect after connection loss for PostgreSQL.
1 post - 1 participant
]]>This is a major rewrite from v0.1. Here’s what changed:
Zero dependencies — Replaced the ocamllex/menhir parser with a hand-written implementation and removed the ptime dependency entirely. The library now only requires ocaml and dune.
New type-safe API — The parsed result is now a polymorphic variant tagged by format (IMF, RFC850, ASCTIME), so you always know which HTTP date format was parsed. The dayname, date, time, and datetime types are all exposed in the public API.
Serious testing — Added property-based tests with alcobar and AFL fuzz testing infrastructure to build confidence in the hand-written parser.
If you’re working with HTTP headers in OCaml, give it a try:
opam install http-date
https://github.com/bikallem/http-date
1 post - 1 participant
]]>==> dune-workspace <==
(lang dune 3.21)
(context default)
(context (default (name profiling) (instrument_with landmarks)))
==> bin/dune <==
(executable
(name main)
(public_name vmax)
(instrumentation (backend landmarks))
(libraries vmax server cmdliner))
==> lib/dune <==
(library
(name vmax)
(instrumentation (backend landmarks))
(libraries ptime mustache fit utils expr yojson))
A dune build now builds
./_build/default/bin/main.exe
./_build/profiling/bin/main.exe
Observations so far:
OCAML_LANDMARKS=output=landmarks.txt,time,allocation is set or not. This suggests the setup above is missing something or the build step requires something that I am missing../_build/installcontains both binaries - but which one gets installed when doing a dune install?I had Landmarks working when I used
(preprocess
(pps landmarks-ppx --auto))
but this makes it difficult to switch between profiling and no profiling.
2 posts - 1 participant
]]>atdml --mode jsonlike app_config.atd generates the OCaml code that converts a JSON-like AST containing source locations into your config data structure.An example is included in the distribution. It uses the recently released YAMLx library.
Note that YAMLx needs generous sponsors to fund the work that was done and unlock a permissive ISC license for everyone. Or you can accept the terms of the AGPL license if you’re happy to share your application’s source code with the users.
Other input file formats or other YAML libraries can be used similarly with minimum effort as long as source locations are available. For this, you’ll need a bridge that converts your AST into the JSON-like AST.
1 post - 1 participant
]]>The big change in this release: ocsigen-i18n is no longer tied to Eliom. It can now be used in any OCaml project, with optional extensions for Tyxml and Eliom when you need them.
Ocsigen-i18n was initially written by @sagotch from Be Sport. This release has been made possible thanks to the work of Habib, funded by IRILL.
opam install ocsigen-i18n
Translations are written in a plain TSV file (one key per line, one column per language):
foo This is a simple key. Ceci est une clé toute simple.
a_human a human un humain
bar I am {{x}}. Je suis {{x}}.
baz There {{{c?are||is an}}} apple{{{c?s||}}} here! Il y a {{{c?des||une}}} pomme{{{c?s||}}} ici !
bu I am {{x %s}} ({{n %d}}). Je suis {{x %s}} ({{n %d}}).
The mini-templating language supports:
{{x}} a string variable ~x{{x %d}} a typed variable using the given format specifier{{{c?yes||no}}} an optional boolean ?c switching between two stringsThen in your code, a PPX extension turns [%i18n key] into the right call:
print_endline [%i18n foo];
print_endline [%i18n bar ~x:[%i18n a_human]];
print_endline [%i18n baz ~c:(nb > 1)];
print_endline [%i18n bu ~x:"Jean-Michel" ~n:42];
(* Switching language explicitly *)
print_endline [%i18n foo ~lang:My_i18n.Fr];
The current language is held in a mutable reference you can swap (or replace with an Eliom scoped reference if you need per-session/tab languages).
The tool plugs into Dune very naturally. Generate the OCaml module from your TSV file with a rule:
(rule
(target example_i18n.ml)
(deps example_i18n.tsv)
(action
(run %{bin:ocsigen-i18n} --languages en,fr --input-file %{deps}
--output-file %{target})))
And wire the PPX in your library/executable:
(preprocess (pps ocsigen-i18n -- --default-module Example_i18n))
That’s all you need for a plain OCaml project.
Pass --tyxml to the generator and the same [%i18n key] expression now produces a list of Tyxml HTML elements instead of a string:
(* Builds an HTML fragment, ready to drop into a Tyxml tree *)
let greeting = [%i18n bar ~x:[%i18n a_human]]
Variables can themselves be lists of HTML nodes, so you can mix translated text with markup naturally:
[%i18n bar ~x:[ txt "Jean-Michel ("
; txt (string_of_int id)
; txt ")" ]]
If you need a plain string in Tyxml mode (for an attribute, for instance), just prefix with S.:
[%i18n S.bar ~x:[%i18n S.a_human]] (* string output *)
For client–server Eliom apps, pass --eliom. The generator emits an .eliom file (so the same translations are available on both sides), implies --tyxml, and adds [@@deriving json] on the language type so you can serialise it across the wire:
(rule
(target example_i18n.eliom)
(deps example_i18n.tsv)
(action
(run %{bin:ocsigen-i18n} --eliom --languages en,fr --input-file %{deps}
--output-file %{target})))
You can split translations across several files. The PPX uses your module path to find the right one:
[%i18n foo] (* default module *)
[%i18n MyI18n.foo] (* MyI18n.foo *)
[%i18n MyI18n.S.bar ~x:[%i18n S.foo]]
Optional --prefix / --suffix flags let you keep call-sites concise ([%i18n Feature.foo] → Pr_Feature_i18n.foo ()).
Happy translating!
2 posts - 2 participants
]]> Cmd.make (Cmd.info "TODO" ~version:"%%VERSION%%") @@
...
so I guess there should be some kind of preprocessing to replace that %%VERSION%% string with the correct value, taken from a “single source of truth”, but I cannot find either. What/where should be that single source of truth, and what should be that expansion mechanism?
One candidate for the source of truth is probably the version stanza in dune-project?
As for the mechanism, I saw something a bit different in some online projects, with dune files with
(rule
(targets version.ml)
(action
(with-stdout-to %{targets}
(echo "let version = \"%{version:my_project}%\""))))
which would allow for instance to write in my code
Cmd.make (Cmd.info "TODO" ~version:Version.version) @@
...
But unfortunately, that does not display the version in the version stanza, but another string that I cannot make sense of, like 2b8df27-dirty%.
Is there a “canonical” or at least a preferred way to obtain a version number in an OCaml project?
Thank you.
10 posts - 5 participants
]]>caml_alloc_tuple lead me to “Fatal error: allocation failure during minor GC”.caml_alloc_shr in the manual but it looks like it is not the one: it allocated large blocks in another place, not everything. (Also, it crashes my program, but I hope it is not relevant.)
7 posts - 4 participants
]]>We are happy to announce the double release of merlin 5.7.0-504 and ocaml-lsp 1.26.0.
The new releases contains exciting changes such as improvements to type enclosing behaviour on various class and object related items, signature help improvements and several several bug fixes.
Please do not hesitate open issues on our bug trackers if you encounter any problems:
2 posts - 2 participants
]]>5 posts - 4 participants
]]>Until now, when teaching at ENSEEIHT, we haven’t been insisting on typing (global) let definitions, leaving typing annotations to val declarations.
However, we are pondering adding type annotations on let definitions. Specifically explicitly polymorphic annotations (probably using the ‘a . ... syntax). There are several reasons:
let definitions, a non-quantified 'a does not stand for polymorphism, contrarily to declarations (we don’t cover type systems in this course and students tend to think that 'a is necessarily polymorphic).val since v4.14.0 so we now have a homogeneous notation for definitions and declarations.Another possibility would be to only use the type a . ... annotation which combines polymorphic recursion with locally-abstract types. Since, later in the course, we also introduce GADTs, this would allow us to teach only one notation, and since this isn’t a theory course, this would avoid difficulties trying to explain differences between syntaxes for polymorphic recursion, locally abstract types and their combination. We would lose the homogeneity with the notation in val though. (Also, as is discussed in this Github issue, the locally-abstract-type notation is probably the most user-friendly among the three possibilities.)
I’d like to know the stance of OCaml teachers or type-system experts on this topic. What would you avoid, what would you advise, how do you address this topic?
1 post - 1 participant
]]>ocaml-letsencrypt implements challenges needed to obtain a certificate via the three ways: DNS, HTTP and ACME-TLS. It therefore enables a website deployment strategy (implemented with Vif, for example) to be provided entirely in OCaml.
We use it within our cooperative for projects such as:
dns-letsencrypt-secondary, a unikernel that acts as a secondary DNS server capable of performing the DNS challenge for a specific domain and uploading the certificate as a TLSA record. It generally works in tandem with our primary-git, our primary DNS server.contruno, a unikernel that acts as a TLS reverse proxy and is capable of performing HTTP challenges. The latter is currently being completely rewritten using mnet.Good luck with deploying your websites, and happy hacking! Here’s hoping that jws also finds its place as a library within the OCaml ecosystem.
2 posts - 2 participants
]]>bin_there and ppx_bin_there. They should be kept synchronized, but the latter depends on the former.
What’s the best way to push to opam? First bin_there, then wait until it’s merged, then ppx_bin_there?
Ideally, I’d like to publish automatically from my CD, is this a possible flow?
5 posts - 5 participants
]]>