OCaml
ocaml.org
OCaml
@ocaml.org
Reading the Gas Meter
My gas supplier has tried and failed to install a smart gas meter, so I’ll give it a go myself.
dlvr.it
November 26, 2025 at 3:37 AM
Gagallium : Testing a priority queue with Monolith
A priority queue is a data structure whose specification is non-deterministic: indeed, if a priority queue contains several key-value pairs whose key is minimal, then any such pair can be legally returned by pop. In this blog post, I describe how to test an OCaml implementation of a priority queue using Monolith. Suppose that we have written (or someone has given us) an implementation of (immutable) priority queues. The signature of this module might look like this (LeftistHeap.mli): module Make (Key : sig type t val compare: t -> t -> int end) : sig (**A key. *) type key = Key.t (**An immutable priority queue. *) type 'a t (**[empty] is the empty queue. *) val empty : 'a t (**[singleton k v] is a singleton queue containing the key-value pair [(k, v)]. *) val singleton : key -> 'a -> 'a t (**[merge q1 q2] merges the queues [q1] and [q2]. The result is a new queue. *) val merge : 'a t -> 'a t -> 'a t (**[pop q] extracts a key-value pair whose key is minimal out of the queue [q]. [None] is returned only in the case where [q] is empty. *) val pop: 'a t -> ((key * 'a) * 'a t) option end The implementation of this module might be based on Chris Okasaki’s leftist heaps, a simple and beautiful data structure (LeftistHeap.ml). But this does not matter. Let’s see how to test this implementation as a black box using Monolith (paper). Monolith expects us to provide a reference implementation of a priority queue. This reference implementation does not need to be very efficient; what matters is that it should be simple and correct. The simplest possible approach is to use an unsorted list of key-value pairs, along the following lines (reference.ml) : type t = (Key.t * Val.t) list let empty : t = [] let singleton k v : t = [(k, v)] let merge q1 q2 : t = q1 @ q2 (* missing [pop], for now *) Here comes the interesting part. pop is a non-deterministic operation: if a priority queue contains several minimal key-value pairs, then any of them can be legally returned by pop. In such a situation, the candidate implementation of pop makes a choice. It would not make sense for the reference implementation of pop to also make a choice: because the two choices might be different, the candidate and the reference could become out of sync. Instead, we want the reference implementation of pop to check that the choice made by the candidate implementation is legal and to obey this choice, that is, to make the same choice. Therefore, the reference implementation of pop cannot have type t -> ((Key.t * Val.t) * t) option, as one might expect. Instead, it must take two arguments, namely the queue on the reference side and the result returned by pop on the candidate side. It is expected to return a diagnostic, that is, an indication of whether this candidate result is valid or invalid. The type diagnostic is defined by Monolith (documentation); its constructors are Valid and Invalid. The reference implementation of pop can be written as follows (link): let pop (q : t) (result : ((Key.t * Val.t) * _) option) : (((Key.t * Val.t) * t) option) diagnostic = match result with | Some (kv, _cq) -> (* The candidate has extracted the key-value pair [kv] and has returned a candidate queue [_cq] that we cannot inspect, as it is an abstract data structure. Fortunately, there is no need to inspect it. *) (* Check that the key-value pair [kv] chosen by the candidate is a minimal element of the queue [q], and return [q] minus [kv]. *) handle @@ fun () -> let q = remove_minimal kv q in valid (Some (kv, q)) | None -> (* The candidate has returned [None]. Check that the reference queue [q] is empty; if it isn't, fail. *) if q = [] then valid None else invalid @@ fun _doc -> format "(* candidate returns None, yet queue is nonempty *)" The auxiliary function remove_minimal kv kvs (link) checks that the key-value pair kv is a minimal element of the list kvs and returns this list deprived of this element. It fails, by raising an exception, if kv is not in the list or not minimal. The auxiliary function handle (link) handles the exceptions raised by remove_minimal and returns an invalid diagnostic in these cases. The reference implementation is now complete. There remains to tell Monolith about the operations that we want to test. For each operation, we must provide a specification (that is, roughly, a type), a reference implementation, and a candidate implementation. This is done as follows (test.ml): let spec = t in declare "empty" spec R.empty C.empty; let spec = value ^> key ^> t in declare "singleton" spec R.singleton C.singleton; let spec = t ^> t ^> t in declare "merge" spec R.merge C.merge; let spec = t ^> nondet (option ((key *** value) *** t)) in declare "pop" spec R.pop C.pop; In the case of pop, we have used the combinator nondet (documentation) to tell Monolith that pop is a non-deterministic operation, whose reference implementation is written in the unusual style that we have shown above. There remains to run the test, which runs forever and prints the problematic scenarios that it detects. It is worth noting that the result of pop is a composite value: it is partly concrete, partly abstract, as it is an (optional) pair of a key-value pair (concrete data that can be observed) and a priority queue (abstract data that cannot be directly observed). This does not create any problem: Monolith is able to automatically decompose this composite value and submit the priority queue component to further testing. To illustrate this, let us intentionally introduce the following bug in the candidate implementation: let pop q = match pop q with | Some (kv, q') -> ignore q'; Some (kv, q) (* return the original queue, unchanged *) | None -> None Then, the test immediately fails and prints the following scenario: (* @03: Failure in an observation: candidate and reference disagree. *) open Bitsets.LeftistHeap.Make(Int);; #require "monolith";; module Sup = Monolith.Support;; (* @01 *) let x0 = singleton 6 11;; (* @02 *) let (Some ((_, _), x1)) = pop x0;; (* @03 *) let observed = pop x1;; (* candidate returns (6, 11), which does not exist *) The priority queue returned by the first pop operation is extracted, named x1, and submitted to a second pop operation, where an observable problem appears.
dlvr.it
November 25, 2025 at 7:34 PM
A Vision for OCaml in the AI Era - Thibaut Mattio - FUN OCaml 2025
A Vision for OCaml in the AI Era Nicolás Ojeda Bär ABSTRACT AI is reshaping software development, yet OCaml's adoption in AI workflows remains limited. This talk explores how the OCaml community can proactively embrace AI to make OCaml a productive language in the AI era—both for writing AI applications and for AI-assisted development. We examine two critical questions: First, how can OCaml become a viable alternative to Python for machine learning? By addressing ecosystem breadth and developer experience, we can solve real problems—from unifying research and production codebases to enabling scalable deployment with OCaml's performance guarantees. Second, how can we make OCaml more productive for AI-assisted development? Strong type systems and integrated tooling give OCaml a natural advantage for coding agents, but we need specialized, open-source tools built for the OCaml ecosystem. This talk introduces Raven, a modern scientific computing ecosystem for OCaml that mirrors Python's ML stack, and Spice, an upcoming local-first, OCaml-native coding agent. Beyond tooling, we'll discuss how the community can anticipate changes in onboarding, documentation, and workflows to ensure OCaml thrives in an AI-driven future—while maintaining equal opportunity and access for all developers. Session page: https://fun-ocaml.com/2025/a-vision-for-ocaml-in-the-ai-era/ Connect with FUN OCaml: Twitter: https://x.com/FunOCaml Bluesky: https://bsky.app/profile/fun-ocaml.com #ocaml --- Big Thanks(TM) go to our generous sponsors who made FUN OCaml possible! * Octra Labs - https://octra.org/ - Platinum Sponsor * Ahrefs - https://ahrefs.com/ - Platinum Sponsor * Dialo - https://dialo.ai/ - Gold Sponsor * LexiFi - https://www.lexifi.com - Bronze Sponsor * Jane Street - https://www.janestreet.com/ - Bronze Sponsor * LightSource - Volunteer / Organizer Commitment * Tarides - Volunteer / Organizer Commitment
www.youtube.com
November 25, 2025 at 3:38 PM
OCaml Weekly News
Hello Here is the latest OCaml Weekly News, for the week of November 11 to 18, 2025. opam 2.5.0~beta1 Archive: https://discuss.ocaml.org/t/ann-opam-2-5-0-beta1/17469/1 Kate announced Hi everyone, We are happy to announce the first beta release of opam 2.5.0. This version is a beta, we invite users to test it to spot previously unnoticed bugs as we head towards the stable release. Try it! The upgrade instructions are unchanged: For Unix systems bash -c "sh <(curl -fsSL https://opam.ocaml.org/install.sh) --version 2.5.0~beta1" or from PowerShell for Windows systems Invoke-Expression "& { $(Invoke-RestMethod https://opam.ocaml.org/install.ps1) } -Version 2.5.0~beta1" Please report any issues to the bug-tracker. Happy hacking, <> <> The opam team <> <> :camel: Announcing the first release of Alice, a radical OCaml build system Archive: https://discuss.ocaml.org/t/announcing-the-first-release-of-alice-a-radical-ocaml-build-system/17472/1 Steve Sherratt announced I’m pleased to announce the initial release of Alice, a radical, experimental OCaml build system, package manager, and environment manager for Windows, macOS, and Linux. Its goal is to allow anyone to program in OCaml with as little friction as possible. To build your first program with Alice, run: $ alice tools install # Skip this if you already have an OCaml compiler! $ alice new hello $ cd hello $ alice run Compiling hello v0.1.0 Running hello/build/packages/hello-0.1.0/debug/executable/hello Hello, World! The UI is heavily inspired by Cargo. An important distinction between Alice's and Opam's packaging philosophies is that in Alice, the OCaml compiler and development tools are not packages. The alice tools install command will install a pre-compiled (relocatable!) OCaml compiler, a compatible ocamllsp, and ocamlformat user-wide, similar to how rustup installs the Rust compiler and LSP server. This lets you go from zero to OCaml really fast because you don't have to build the compiler from source. This speedup is particularly noticeable on Windows where building the compiler can take upwards of 10 minutes. Alice supports building packages with dependencies on other packages, but currently only local packages are supported, and it can only build Alice packages, not Opam packages. See an example here. I'll probably add Opam compatibility in the future. It's still early days and a lot is missing before Alice could feasibly be used for real projects. If you want to try it out anyway, install the alice Opam package, the github:alicecaml/alice Nix flake, or run the interactive install script: curl -fsSL https://alicecaml.org/install.sh | sh More details about installing Alice are here. If you want read more, check out the blog.
dlvr.it
November 24, 2025 at 11:29 PM
Analyzing Programs with SMT Solvers - Tikhon Jelvis - FUN OCaml 2025
Analyzing Programs with SMT Solvers Tikhon Jelvis (Semgrep) ABSTRACT We can use SMT solvers like Z3 to analyze programs and answer difficult questions about our code. By generating and solving complex constraints, we can improve error message localization, attack cryptographic protocols, check refinement types, search for proofs, verify invariants, compare programs against specifications or even synthesize code. Z3 is the goto state-of-the-art SMT solver, available under and open source license and has a first-class OCaml library. In this talk, I'll walk through a simple OCaml program to generate SMT constraints that represent a bounded model of programs in a simple imperative language. Algebraic data types and pattern matching are a perfect fit for this kind of work. Session page: https://fun-ocaml.com/2025/analyzing-programs-with-smt-solvers/ Connect with FUN OCaml: Twitter: https://x.com/FunOCaml Bluesky: https://bsky.app/profile/fun-ocaml.com #ocaml --- Big Thanks(TM) go to our generous sponsors who made FUN OCaml possible! * Octra Labs - https://octra.org/ - Platinum Sponsor * Ahrefs - https://ahrefs.com/ - Platinum Sponsor * Dialo - https://dialo.ai/ - Gold Sponsor * LexiFi - https://www.lexifi.com - Bronze Sponsor * Jane Street - https://www.janestreet.com/ - Bronze Sponsor * LightSource - Volunteer / Organizer Commitment * Tarides - Volunteer / Organizer Commitment
www.youtube.com
November 23, 2025 at 3:36 AM
OCaml Weekly News
Hello Here is the latest OCaml Weekly News, for the week of November 04 to 11, 2025. Opam-repository, signed! conex is in beta now :) Archive: https://discuss.ocaml.org/t/opam-repository-signed-conex-is-in-beta-now/17461/1 Hannes Mehnert announced Dear everyone, we’re proud to announce that conex, in an initial version, is now live! You can receive signed updates of the opam-repository. A quick guide: To setup opam with conex on your machine, you need to install conex (and/or conex-mirage-crypto, see below) - currently only available as opam package (sorry about the bootstrap problem - hopefully it will be integrated into opam at some time). Then, in your “\~/.opam/config” you need to specify the repository-validation-command: repository-validation-command: [ "/path/to/conex_verify_mirage_crypto" "--quorum=%{quorum}%" "--trust-anchors=%{anchors}%" "--repository=%{repo}%" {incremental} "--dir=%{dir}%" {!incremental} "--patch=%{patch}%" {incremental} "--incremental" {incremental} ] Instead of “conex_verify_mirage_crypto”, you can as well use “conex_verify_openssl” (fewer dependencies, calls out to OpenSSL as the cryptographic provider, is slower). Then you can run opam repo add conex-robur https://conex.robur.coop 1 sha256=ad5eb0e4a77abfbc6c1bb5661eba46049404e0222588dd059c87f12436d41a28. Thereafter you can opam repo remove default, and then you’re only using signed metadata. The “1” is the quorum of root keys for signatures to be valid, the “sha256=ad5eb0e4a77abfbc6c1bb5661eba46049404e0222588dd059c87f12436d41a28” is the hash of the public root key. Read the full article at https://hannes.robur.coop/Posts/ConexRunning – happy to receive feedback. The development of conex is supported by the OCaml Software Foundation and by ahrefs.
dlvr.it
November 22, 2025 at 7:34 PM
From OCaml 4 to 5 and from Parmap to Effects: A legacy code transition story - FUN OCaml 2025
From OCaml 4 to 5 and from Parmap to Effects: A legacy code transition story SPEAKERS Nathan Taylor (Semgrep) Nat Mote (Semgrep) ABSTRACT OCaml 5's support for shared memory parallelism and effects-based concurrency opens up new ways for developers to build new OCaml programs for modern hardware, but it isn't trivial to migrate existing software to this new world. We've spent the last few months porting a large (~3MM LoC) OCaml codebase from process-based parallelism to multi-domain Eio, and learned a lot along the way. In this talk, we'll discuss the fundamentals of shared-memory parallism, our incremental migration approach which used a combination of static and dynamic analysis, the pitfalls (both expected and unexpected) that we encountered along the way, and some lessons that others can adopt for their own journey. Session page: https://fun-ocaml.com/2025/from-ocaml-4-to-5/ Connect with FUN OCaml: Twitter: https://x.com/FunOCaml Bluesky: https://bsky.app/profile/fun-ocaml.com #ocaml --- Big Thanks(TM) go to our generous sponsors who made FUN OCaml possible! * Octra Labs - https://octra.org/ - Platinum Sponsor * Ahrefs - https://ahrefs.com/ - Platinum Sponsor * Dialo - https://dialo.ai/ - Gold Sponsor * LexiFi - https://www.lexifi.com - Bronze Sponsor * Jane Street - https://www.janestreet.com/ - Bronze Sponsor * LightSource - Volunteer / Organizer Commitment * Tarides - Volunteer / Organizer Commitment
www.youtube.com
November 21, 2025 at 3:37 PM
Conex, securing the opam-repository, is now in production
Written by hannes TL;DR: The long-awaited conex timestamp and snapshot service is now in beta. Test it, we need feedback :) Introduction The community-maintained opam-repository is a git repository hosted on GitHub, which dozens people and bots have write access to. The opam.ocaml.org servers, which deliver the opam-repository by default, are operated on scaleway machines -- and we couldn't find any documentation of how this service is run and who has which privileges on it. The opam-repository contains package metadata in the form of package names, their dependencies, and their source tarballs with sha256 (or sha512) checksums of these. An attacker could either modify the GitHub repository (e.g. changing a checksum, introduce a dependency, introduce an upper bound to a vulnerable version, ...); the opam.ocaml.org servers; or sit on the network connection between you and the server and prevent any update. Your opam client won't notice this. Conex is a security system and implementation, based on tuf, to prevent a lot of attack vectors (see TUF attacks to get an overview). We're proud that after a decade of our initial announcement we got it into shape. What we announce here is only the first minimal viable version: a service that serves the opam-repository, updating hourly, and signing this repository. The timestamp service signs the repository every 5 minutes. So, security-wise this means: instead of trusting the opam.ocaml.org servers, you have to trust the opam-mirror, and us at Robur for running it without introducing malicious data. With this minimal version, we're not yet at a stage where OCaml package authors sign their releases, but we're making good progress towards securing the opam-repository. For the end-to-end signing we need to figure out workflows, especially taking the amazing work of the opam-repository maintainers into account. Security As mentioned, the conex.robur.coop service is run as a MirageOS unikernel by Robur. The source code is public. The running binary is available from our reproducible build infrastructure. What we prevent in this version are Fast-forward attacks: an attacker who compromised signing keys can increase the version to the maximum value. This means a client would not be able to update to a version recovering from the key compromise. We implemented the epoch system, which requires a more privileged key to sign, and can recover from that maximum version. We can also rotate keys when they are compromised. We also make Indefinite freeze attacks noticable: the timestamp service signs every 5 minutes its data -- so if your client receives a signature that is outdated, this can be detected. The Mix-and-match attacks are avoided by our snapshot service, which signs all signatures of the opam-repository (at the moment, we only have a single maintainer key). This means an attacker cannot provide you with old metadata of package "a" and newer metadata of package "b". Rollback attacks are also protected, since we have version numbers in the signing metadata, and any version decrement (or modification without increment) leads to a verification failure. Vulnerability to key compromises are avoided, since we can rotate keys. The root key is kept offline. If you have any questions about the security implications, please reach out. Setup your conex repo To setup opam with conex on your machine, you need to install conex (and/or conex-mirage-crypto, see below) - currently only available as opam package (sorry about the bootstrap problem - hopefully it will be integrated into opam at some time). Then, in your "~/.opam/config" you need to specify the repository-validation-command: repository-validation-command: [ "/path/to/conex_verify_mirage_crypto" "--quorum=%{quorum}%" "--trust-anchors=%{anchors}%" "--repository=%{repo}%" {incremental} "--dir=%{dir}%" {!incremental} "--patch=%{patch}%" {incremental} "--incremental" {incremental} ] Instead of "conex_verify_mirage_crypto", you can as well use "conex_verify_openssl" (fewer dependencies, calls out to OpenSSL as the cryptographic provider, is slower). Then you can run opam repo add conex-robur https://conex.robur.coop 1 sha256=ad5eb0e4a77abfbc6c1bb5661eba46049404e0222588dd059c87f12436d41a28. Thereafter you can opam repo remove default, and then you're only using signed metadata. The "1" is the quorum of root keys for signatures to be valid, the "sha256=ad5eb0e4a77abfbc6c1bb5661eba46049404e0222588dd059c87f12436d41a28" is the hash of the public root key. Set it up yourself You can as well run your own conex opam-repository. You can either download the binary from above, or compile from source: $ git clone git://git.robur.coop/robur/opam-mirror -b conex $ cd opam-mirror/mirage $ mirage configure -t <my-target> # use unix, hvt, spt, xen, virtio, ... $ make Setup cryptographic keys The next step is to setup cryptographic keys: # generate root key, and root file $ conex_key --id root $ conex_root create && conex_root add-key --id root # generate maintainer key, and add it to the root file $ conex_key --id maintainer $ conex_root add-to-role --role maintainer --id maintainer # generate snapshot key, and add it to the root file $ conex_key --id snapshot $ conex_root add-to-role --role snapshot --id snapshot # generate timestamp key, and add it to the root file $ conex_key --id timestamp $ conex_root add-to-role --role timestamp --id timestamp # sign the root file with the root key $ conex_root sign --id root # truncate the root file (needed for the unikernel to be a multiple of 512 bytes) $ truncate -s 2k root Start the unikernel The unikernel needs another block device (scratch space) to store data to persist across reboots. $ truncate -s 2G conex-data Now you can run the unikernel, first it should initialize the block device (please keep in mind this is for the hvt version of the unikernel, other deployment targets need other command lines): $ solo5-hvt --net:service=tap0 --block:tar=conex-data --block:root=root -- dist/mirror.hvt --initialize-disk --index-size=100 And then run the unikernel for good, it will start cloning the opam-repository from GitHub, and add signatures: $ solo5-hvt --net:service=tap0 --block:tar=conex-data --block:root=root -- dist/mirror.hvt --ipv4=10.0.42.2/24 --ipv4-gateway=10.0.42.1 --skip-download --target-key="..." --snapshot-key="..." --timestamp-key="..." Where the keys you provide is the raw data from "~/.conex/timestamp*" -- only the middle line (not the "----- BEGIN PRIVATE KEY-----" and "----- END PRIVATE KEY-----" lines). Conclusion and future We delivered the minimal version of conex, and are keen to receive feedback. We are also keen to push conex further, and allow opam package authors to sign their releases. This requires additional code, but also documenting and adapting workflows of opam-repository maintainers. We will also integrate conex signatures into commonly used publication tools opam-publish and dune-release. The development of conex is supported by the OCaml Software Foundation and by ahrefs.
dlvr.it
November 21, 2025 at 3:36 AM
OCaml Roundup: October 2025 [ocaml-roundup-october-2025]
The contribution period for this year's Outreachy round took place for most of the month of October. This year I am excited to be going back to a Geocaml project, working primarily to add writing capabilities to ocaml-tiff. As per usual, I have been extremely pleased with the level of interest in the various OCaml projects being offered. Whilst we are still deliberating on our choices for interns this year, I won't say much more on the contributions, but if you want to get involved please do reach out! OCaml is a great language for geospatial work, striking a balance between reasonable performance for numerical code and also a rich type system for expressing complicated geospatial data-structures. Ppxlib and the Future of Compiler Support Nathan has put a tremendous amount of effort into laying a roadmap this month for how ppxlib, going forward, will support future compilers. This is in response to the calamity that was bumping the internal AST to 5.2, and whilst I think we are adding a good deal more complexity, it will hopefully keep the ppx universe on an even keel for longer periods of time. The first substantial PR for this modern world of ppxlib is in review! Aside from that I also debugged an issue that lead to ppxlib OOM-ing machines... of course, any recursive fold-like loop where the accumulator keeps growing is likely to do that! It was nice to be able to crack out memtrace to be able to hunt it down: A common tasks for many a CLI tool is to scan a directory of files, filter out the ones you are looking for and apply some transformation to said files. In fact, that is what Graft and Forester both do! A standard approach to this involves a recursive dance of readdir and stat, the former lists the entries in a directory whilst the latter lets you know what kind of file is at a particular path (e.g. symbolic link, directory etc.). This has a few downsides: We're making two systems calls (pretty much one of the most expensive things your program can do). If a directory contains thousands of files, we end up allocating a big list of strings after returning from readdir. On Linux, we can get around most of that with getdents64(2) which allows us to incrementally read a directory. Whatsomore, this system call also returns the file type so there's no need to do an extra stat! I prototyped an implementation of this called Eio.Path.walk which looks like: val walk : _ t -> ( ( File.Stat.kind * string) Seq.t -> 'a) -> 'a (** [walk t] traverses the directory [t] producing a sequence of results. *) We effectively halve the number of system calls. In a benchmark that traverse deeply-nested directories with plenty of files, a full traversal whent from 1.46s to 110ms. I have since rejigged the implementation to automatically recurse for the user into subdirectories (not that the signature is unchanged) which does make it tricky to control the number of open file descriptors compared to if the user were in control. OxCaml Experiments I have included here a transclusion from my ICFP post about an experiment using unboxed 32-bit integers in OCaml's Uring library. Jane Street were a big presence at ICFP 2025, carting along with them a shiny new OCaml compiler: OxCaml. If have been playing around with OxCaml recently but nothing outside toplevels in Javascript. Until now! After talking to David, I spent some time converting a small corner of the ocaml-uring library to use a part of OxCaml. In particular making the following change: module Heap = struct type ptr = int32# (* ... *) end type cqe = { user_data_id : Heap.ptr; res : int32#; } The idea being that a completion queue entry (a notification that some operation has completed) could be fully represented using 64 bits (two 32-bit, unboxed values). You can see how this impacted the library! I'm not certain about this change (and I'm sure I did it wrong) but it was nice to realise OxCaml gives you this kind of control. However, I am worried about the ergonomics of manipulating values like int32# and the temptation to case it into an int (presumably losing a good portion of the value of having an unboxed value in the first place). I sent a few PRs to the OxCaml repository to get the toplevel working again and lucky the underlying culprit for why patches were disappearing was found. Watch this space for a new toplevel in the browser soon. In addition to the Outreachy work I mentioned above, I also found the time to keep the Geocaml project ticking along. A few projects saw some much-needed love including: A modernisation of the bindings to PROJ. PROJ is probably the most used and tested code for working with geospatial projections. Any time you get a sateillite image or a blob of GeoJSON, the geospatial data will normally have some coordinate reference system (CRS) with it detailing exactly what the numbers that tell you where something is mean. One of the most common being WGS84 (used by GPS and GeoJSON for example). Each CRS has its benefits and drawbacks, and often you will need to convert between them. That is what PROJ does. Well-known Text (WKT) is a very simple encoding of geospatial objects and CRSs. For example, a polygon might look like: Polygon ((10 10, 10 20, 20 20, 20 15, 10 10)) I used bytesrw to build a simple codec for the WKT format. It is probably about time to write the basic geospatial object library too, something similar to Rust's geo library. A lot of people in my group are excited about Tessera. They have made it easy to used the Tessera embeddings via a python library called geotessera. In true OCaml-at-heart spirit, I tried my hand at porting this library. I had a good deal of success, converting the data into Nx array, extracting the CRS and transform out of the GeoTIFF landmasks using ocaml-tiff, reading datasets using ocaml-geojson etc. This made me excited but also keen to improve the APIs of most of these libraries too. I feel geocaml is at a critical mass where we can start to have this useful feedback loop for the libraries because there is enough functionality to actually use them!
dlvr.it
November 20, 2025 at 11:29 PM
Robur's blog - A stem engine and a search engine for OCaml
To rummage through your emails
dlvr.it
November 20, 2025 at 7:34 PM