In this opinion piece, I will try to briefly share my encounter with the language and list its advantages — organized into several sections covering the language itself, its ecosystem, and its community. I will also attempt to debunk some popular myths (or misconceptions) found on the Internet. For the sake of transparency, it is important to note that, at the time of writing, my professional work involves working for and on the OCaml ecosystem. However, readers who have followed me for several years can attest that I was promoting the language long before I was paid to work on the OCaml ecosystem, sometimes rather immoderately.
Foreword
First, this article will explain why I personally believe that OCaml is a relevant choice in many contex…
In this opinion piece, I will try to briefly share my encounter with the language and list its advantages — organized into several sections covering the language itself, its ecosystem, and its community. I will also attempt to debunk some popular myths (or misconceptions) found on the Internet. For the sake of transparency, it is important to note that, at the time of writing, my professional work involves working for and on the OCaml ecosystem. However, readers who have followed me for several years can attest that I was promoting the language long before I was paid to work on the OCaml ecosystem, sometimes rather immoderately.
Foreword
First, this article will explain why I personally believe that OCaml is a relevant choice in many contexts. My goal is not specifically to convince you—although that would be a very welcome side effect — and it’s quite likely that many of the arguments I present will also apply to other languages!
Also, very often, when I suggest OCaml to people who want to explore new languages or try out solutions written in OCaml, I’m kindly told that I’m always promoting OCaml. It’s amusing to notice that when the suggestions involve languages adopted by default, like JavaScript, or more recent ones like Rust or Go, they tend to trigger fewer reactions. This is probably because people implicitly assume that proposing a lesser-known language leans toward irrationality and personal preference. From my point of view, suggesting OCaml is, in many cases where fine-grained memory control is not needed, just as relevant as suggesting Rust (and probably more so).
To wrap up this preface, many people first encountered OCaml (or Caml Light) during their undergraduate studies or in preparatory classes, often using it in contexts far removed from industry. As for me, I started getting interested in OCaml much earlier, thanks to the Site du Zéro, where a small community of functional programming enthusiasts promoted less mainstream languages like OCaml, Erlang, and Haskell. My interaction with OCaml at university was just a bonus.
Other resources
I’m not the first to document the reasons for choosing OCaml. There are many other resources that, in my opinion, are also worth checking out, and they show that OCaml users are generally very satisfied — so much so that they’re motivated to share how and why we chose the language as our main technology:
“Why OCaml?”, the prologue of the book Real World OCaml, which presents factual advantages of using OCaml (and whose introduction includes a timeline). While the book is excellent in many respects, I’ve gotten into the habit of not recommending it because I find its usage approach quite biased, suggesting libraries by default that aren’t necessarily widely accepted in the community.
“Better Programming Through OCaml”, the prologue of the book (accompanied by videos) OCaml Programming: Correct + Efficient + Beautiful, which mainly explains how learning OCaml can improve a developer’s skills in other, more popular technologies. The book is fairly recent, and it’s the one I now recommend as the go-to resource for getting started with OCaml.
Talk: “Why OCaml?”, a presentation by Yaron Minsky, CTO of Jane Street—an industrial user of OCaml and one of the global leaders in finance. Yaron is also one of the authors of Real World OCaml and the originator of the widely quoted phrase in the statically typed programming languages world, “Make illegal states unrepresentable”. The talk offers plenty of insights into Jane Street’s motivations for choosing OCaml.
“OCaml for Fun & Profit: An Experience Report”, presented by Tim McGilchrist at Yow 2023. After a rich introduction to the language, it covers some very concrete use cases of OCaml in production — with fun and profit.
“Replacing Python for 0Install” by Thomas Leonard. This series of articles is, in my view, incredibly interesting. The author of 0Install, a decentralized, cross-platform software installation system (a slightly older alternative to Nix), was looking for a language other than Python for a new version’s implementation (the reasons for replacing Python are also documented here) and carried out a thorough, methodical comparison of several candidates: ATS, C#, Haskell, Go, Rust, and OCaml, alongside Python. Years later, I’m still impressed by the rigor and nuance of this series, which I highly recommend. There are probably other resources and testimonials, notably on the official website, which features both industrial and academic case studies. There are also articles expressing the frustration OCaml can cause. I’m aware that OCaml is not perfect—nor do I believe any technology is perfect. I’ll likely refer to some of these articles (implicitly or explicitly) in the section on myths and in the conclusion, where I’ll try to explain in which contexts I don’t find OCaml to be a relevant choice.
OCaml as a language
Before diving into the features offered by the language, I’d like to start with a point that I believe is essential. OCaml is a programming language that originated from research and is used by industrial users. This duality is important because it provides the language with two key advantages:
Guidance on desirable features as interesting language concepts, supported by advanced research. For example, to my knowledge, OCaml is the first mainstream language to offer native support for user-defined effects, which is the result of cutting-edge research, illustrated by numerous publications.
Guidance on desirable features as tools for industrialization, also backed by research and motivated by practical use cases. For instance, recently, Jane Street, a major industrial OCaml user, proposed the integration of affine sessions, enabling linear resource management (somewhat Rust-like). This intertwining of industrial and academic motivations allows OCaml to offer a collection of solid, useful, and well-defined features. In other words, OCaml is a living language, and since I’ve been using it, I’ve witnessed many developments and additions that I consider highly desirable and that debunk a common assertion against OCaml: the language is only useful for theory or for implementing Coq/Rocq.
Although this was historically true, the motivations provided by industrial users justify the label “An industrial-strength functional programming language with an emphasis on expressiveness and safety.” The opening keynote of the OCaml Workshop 2021 by Xavier Leroy, titled “25 Years Of OCaml,” presents an exhaustive timeline of OCaml’s continuous design, showing the various phases of evolution the language has undergone.
In broad terms, OCaml is a programming language from the ML family, high-level (here, meaning it features garbage collection), statically typed (types are checked at compile time with no implicit conversions), with type inference (also called type synthesis), allowing the compiler to deduce the type of an expression in most cases. This enables programming in both functional and imperative styles.
OCaml also provides an object-oriented programming model and a very rich module system. The language has two compilation schemes: ocamlc, which compiles to a bytecode executable by a virtual machine (portable and efficient), and ocamlopt, which compiles to native machine code (runnable on a wide variety of architectures).
Moreover, OCaml allows conversion of its bytecode to JavaScript using Js_of_ocaml, enabling very fast interoperability within the OCaml ecosystem (which I use extensively on this website). The same approach is used to produce WebAssembly. For deeper interoperability with the JavaScript ecosystem, Melange takes a somewhat different approach than Js_of_ocaml to generate robust JavaScript.
OCaml is a highly versatile language, and I will now try to present the features and strengths that make it — for me — an ideal tool for building both personal and professional projects, starting with a brief detour into static typing.
On static type checking
When I was preparing, with Bruno, the episode of If This Then Dev dedicated to OCaml — which, in the end, was recorded with Didier — he asked me a question that I found surprising:
“Is it really worth bothering with types when working on a personal project quickly? Even though I can perfectly see the value for production code, for a personal project it seems like a waste of time to me.”
I think there are two main angles to answer this. The first, and most obvious, is that, in principle, I don’t see why a personal project should be any less disciplined than a professional one. When I write software for myself, I could indeed get away with ignoring the corner cases of my implementation. Sure, that’s possible. But that’s probably not what I actually want to do. So, if a language and its compiler let me set up safety nets that force me to account for all the cases in my software, I take them — just like writing unit tests makes development easier, and I don’t see them as a constraint.
But beyond considerations of hygiene in a personal project, I think the negative reputation of static type checking usually stems from a bad experience. Indeed, in languages like C or Java, types are mostly a constraint that can be easily circumvented. In languages that place a strong emphasis on typing — like OCaml, Haskell, F#, Scala, or Rust — types act as safeguards. More importantly, in my view, types also serve as a tool for expressive design. Using them provides safety while also offering an incredibly rich, versatile, and concise way to describe data.
From my experience, even though it’s common to move from a poorly-typed (sorry, the temptation is too strong) to a dynamically typed language — I, for instance, happily transitioned from Java to Ruby — moving from a language with a rich type system, like OCaml or Haskell, makes switching to a dynamically typed language much harder. At present, I don’t know anyone who has seriously used languages like OCaml or Haskell and was happy to return to languages with less sophisticated type systems (though an interesting project can sometimes justify such a technological regression).
This is not just a personal observation; static type checking is central to the broader debate about the evolution of programming languages. Historical languages evolve (or attempt to evolve) to integrate more type checking. For instance, Erlang, as early as the 1980s (before its compiler source was released), experimented with integrating a type system. Java, version by version, enhances features aimed at improving static type verification, such as incorporating sealed families.
Many languages are experimenting with type systems: Ruby with RBS, Crystal (a statically typed language heavily inspired by Ruby), Python with Mypy, Elixir (which revisits Erlang’s past experiments, offering a viable gradual typing approach), and, of course, TypeScript, which has become widely adopted in the JavaScript community.
While all these initiatives are encouraging and clearly move in the right direction, for now, they primarily add safeguards but do not yet serve as expressive design tools.
When it comes to increasingly rich type systems, the White House recently published a report emphasizing the importance of memory safety in software design and… endorsing the use of the Rust language (historically written in OCaml before becoming self-hosted) over C++, clearly showing that even official bodies (often considered outdated) highlight the value of rich type systems. Moreover, the response from Tarides, the company I work for at the time of writing this article, also presents compelling arguments in favor of using OCaml for building critical systems.
In conclusion, static type checking is really valuable and highly recommended, and it’s worth exploring languages with sophisticated type systems (like OCaml) and, why not, going even further by increasingly delving into formal methods.
Features of the language
Even though it’s very tempting to create a massive OCaml tutorial, the goal of this section is to present what makes OCaml, for me, a highly relevant choice for both learning and production. The advantages will therefore be presented (and defended), but this is not a tutorial.
A multi-paradigm language
Nowadays, talking about multi-paradigm languages might seem unnecessary, since a large majority of programming languages favored by industry are already multi-paradigm. However, OCaml is a functional programming language that also supports imperative programming, modular programming, object-oriented programming, and, since version 5.0.0, multi-core programming.
Just as Haskell is widely recognized in the functional programming world, it’s often assumed that adding imperative mechanisms to a language is a bad idea — especially if one is convinced of the benefits of the functional style. From my perspective, there are several perfectly legitimate reasons to use imperative programming when the language allows it:
Readability of an implementation. Sometimes, avoiding mutability requires adding extra plumbing (for example, a State Monad), which can make reading and understanding a program more cumbersome.
Performance. Adding such plumbing can introduce overhead, making the execution of implementations more costly.
Ease of use. A few years ago, Arthur Guillon ceremoniously told me that “OCaml is a lambda calculus that trivially allows effects,” which makes it very effective for tasks like debugging, where printing messages to standard output is simple. While I acknowledge that this is probably not the best way to implement logging, it undeniably provides a comfortable user experience and enables rapid prototyping. In general, OCaml’s dual nature — both imperative and functional — allows you to leverage the advantages of both paradigms in different situations and, of course, to combine them. For example, hidding a module’s imperative nature behind a functional API.
Syntax à la ML
Although syntax is often considered a minor detail, languages in the ML family have a concise, expressive, and readable syntax. Even though this family of syntax can be confusing when coming from more conventional, C-inspired syntax, one gets used to it fairly quickly and can soon realize that it is very consistent and relatively unambiguous. However, if OCaml’s syntax is problematic for you, don’t hesitate to look into ReasonML, an alternative syntax that uses braces.
OCaml is a language that originates from French research, as shown by the history of Caml, primarily designed to implement the proof assistant Coq/Rocq. This origin — and the initial motivations, implementing Coq while also serving as a programming language taught in preparatory classes—creates a certain duality:
The core features were not initially designed with industry in mind. However, this assertion is no longer true, primarily because OCaml has become a language used in industrial contexts. While in the language’s genesis, there were more tools for building a language itself (facilitating the teaching of compiler mechanisms) than tools for building “enterprise” applications, projects from the community motivated by industrial use have enriched the language and its ecosystem, making it a versatile tool suitable for industry. For example, creating a binding with the Tk library led to the integration in the language of named arguments, optional arguments, and polymorphic variants.
The set of paradigms and language features are carefully thought out and well-theorized. Generally, the integration of a feature (or collection of features) results from meticulous research, based on solid theoretical foundations and reviewed by numerous experts in the field (often recognized by the scientific community). This rigor can sometimes slow the introduction of new features but generally ensures their proper functioning and theoretical stability. This theoretical rigor, stemming from OCaml’s undeniable closeness to the research world, means that its various aspects are well documented, illustrated by a large number of publications, and exhibit predictable behavior. From my point of view, this makes OCaml a very wise choice for understanding these different features in depth. For example, I believe OCaml has allowed me to much better understand certain traits or paradigms of programming languages.
Moreover, a great example of how meticulous and rigorous research can support the integration of a language feature is OCaml’s implementation of an object model. Indeed, the thesis of Jérôme Vouillon, Design and Implementation of an Extension of the ML Language with Objects, proposes an innovative object model that integrates very well with type inference by separating the notions of inheritance and subtyping — inheritance being a syntactic notion and subtyping a semantic notion — using row polymorphism to describe structural subtyping relationships, as opposed to nominal subtyping, used by Java, C#, and most popular OOP languages. OCaml’s object model fully adheres to the SOLID principles without any additional ceremony.
Algebraic types
I’ve been quite expansive about the reasons why I value a language with static type checking. However, in my experience, for a statically typed language to be truly usable, the presence of algebraic types is necessary:
Product types: These allow grouping values of heterogeneous types (thus creating a conjunction of heterogeneous types). They are generally present in all mainstream languages (for example, objects, which introduce additional concepts, or tuples and records).
Sum types: These allow constructing a disjunction of heterogeneous value types, with different cases indexed by constructors. While some special cases of sums exist in mainstream languages—like booleans (which are a disjunction of two cases: true and false, i.e., two parameterless constructors) — support for full sum types is often cumbersome in popular languages. For example, Kotlin and Java (and de facto C#) use a construct associated with inheritance relations called sealing. The integration of dedicated sum type syntax also took some time in Scala, which, prior to recent versions, relied on sealed families, making the expression of sums verbose and, in my view, harder to reason about.
Exponential types: These allow describing functions that express types for higher-order functions (functions that can be passed as arguments or returned as results). Coupled with pattern matching and parametric polymorphism (or generics), an algebraic type system is an incredibly expressive tool for describing data structures, the state machine of a program, or modeling a business domain with an appropriate cardinality. Even in the 21st century, where products and exponentials are common, when I use very popular languages, I am often frustrated by the lack of sum types, which forces me to use verbose encodings (increasing the domain’s cardinality). This is particularly noticeable when working with Go and TypeScript.
The appeal of this triad is, in fact, probably one of the reasons (combined with a very ergonomic ecosystem and toolchain) behind the success of Rust. In short, if you intend to build a new programming language with static type checking, please, do not hesitate to include algebraic types!
Finally, there are aspects of OCaml’s type system that I haven’t covered, but which probably deserve dedicated articles. For example, generalized algebraic data types (GADTs), which allow expressing even more invariants.
Modular programming and module language
OCaml, through its ancestor Caml Light, was among the first languages to offer a module system, similar to Standard ML, providing encapsulation and abstraction while supporting separate compilation, in the style of Modula-2. OCaml’s module system is a fundamental aspect of the language, although its complexity can be intimidating. Indeed, in OCaml, it is possible to clearly distinguish the interface (the signature) from the implementation (the structure), thus facilitating encapsulation and documentation, while also allowing function application within the module language.
I find it particularly difficult to address the topic of modules briefly (it’s a subject I’ve wanted to explore on my blog for years). However, here is a list of advantages I see in OCaml’s highly modular approach:
Separate compilation: A key feature that allows efficient compilation of large programs by identifying junction points to optimize parallel and incremental compilation. This approach is leveraged by dune, the recommended build system for OCaml.
Systematic separation of implementation and interface: Offers several significant advantages, including encapsulation and placing documentation in the interface. In my programming workflow, I find this very convenient because I can implement my structure (the module’s implementation) while being guided by type inference and specify its API in the signature (the module’s interface), deciding on the display order and providing clear documentation that doesn’t pollute the implementation space. Additionally, encapsulation allows me to freely define intermediate types inside the structure, for example, to represent a program’s state machine, without letting it escape.
A powerful tool for describing data structures: By abstracting types (hiding their implementation) and combining this with encapsulation, it is possible to describe data structures that maintain invariants. This is why it is common to have a structure/signature pair for each data structure, hiding implementation details through abstraction and encapsulation.
Reusability and sharing: Just as it is possible to describe types in the value language (as seen with algebraic types), it is also possible to describe types in the module language, called translucent signatures, which allow defining the type of a signature without associating it with a structure. These signatures are structurally typed, and coupled with functors (functions in the module language), it is possible to share behavior between modules.
Advanced forms of polymorphism: Including Higher Kinded Polymorphism, available in the module language. In broad terms, you can describe “generics parameterized by generics”. This limitation in languages like F# or Java often motivates the use of heavy encodings to work around the lack. The theory behind module languages in ML-family languages is a vast subject, still evolving, and very difficult to summarize in a single paragraph. However, the introduction of Derek Dreyer’s thesis, Understanding and Evolving the ML Module System, provides an excellent explanation of the purpose and use of modules, illustrated with many examples. I hope to take the time in the coming weeks or months to write more extensively about the module language than I have already attempted, because it could be very educational and, in my view, the topic is extremely interesting!
Dependency injection and inversion
Briefly touching on object-oriented programming in OCaml, I mentioned that OCaml allows, through its language features, a straightforward way to meet the prerequisites for writing SOLID code. The final point I’d like to emphasize is the ease of dependency inversion, achievable through language-provided features. In broad terms, the principle of dependency inversion involves describing dependency lattices using abstractions rather than implementations. This way, dependencies can be injected afterward — making context changes, for example in unit testing, trivially implementable.
OCaml provides (at least) two tools that facilitate this inversion, each useful in different contexts. We will draw inspiration from the very popular teletype example to show how to invert dependencies:
let program () =
let () = print_endline "Hello World" in
let () = print_endline "What is your name?" in
let name = read_line () in
print_endline ("Hello " ^ name)
Even if it might not seem obvious, this program depends on concrete implementations — namely, interactions with standard input and output.
Through modules
The most straightforward approach is to use modules, either as first-class values or by construction, using functors. The duality between signatures and structures makes dependency inversion obvious. For example, to revisit our example, here’s how, using first-class modules, it becomes very easy to depend on an abstract set of interactions. We start by describing the abstract representation of possible interactions:
module type IO = sig
val print_endline : string -> unit
val read_line : unit -> string
end
We can now expect our program function to take a module of type IO as an argument (we’ll call this a handler) and use the functions exported by the module, which in our example is named Handler:
let program (module Handler: IO) =
let () = Handler.print_endline "Hello World" in
let () = Handler.print_endline "What is your name?" in
let name = Handler.read_line () in
Handler.print_endline ("Hello " ^ name)
For example, in the context of unit testing, it’s possible to provide an implementation that logs all the operations called (and mocks the read_line call to fix the returned result). This makes expressing unit tests that verify business logic very easy to implement.
Passing a concrete implementation as an argument to our function amounts to interpreting the program.
Through user-defined effects
OCaml version 5 arrived with a host of new features. However, the biggest advancement is the complete redesign of the OCaml runtime to support multi-core execution. There are several ways to describe concurrent algorithms — for example, using actors or channels. OCaml has chosen to rely on effects, which simplify the management of the program’s control flow. In fact, OCaml allows users to define their own effects, logically called user-defined effects. While they are a powerful tool for describing concurrent programs, they also make it easier to inject dependencies when you want to maintain control, at the handler level, over the execution flow of a program.
Note: In my example, I am using an experimental syntax, just merged into the OCaml main branch, which will likely be available in version
5.3.0of the language.
As with our previous improvement, we first need to describe the set of operations that can be performed. We use the effect construct:
effect Print_endline : string -> unit
effect Read_line : unit -> string
Next, we can write our program in a direct style, by producing effects:
let program () =
let () = Effect.perform (Print_endline "Hello World") in
let () = Effect.perform (Print_endline "What is your name?") in
let name = Effect.perform (Read_line ()) in
Effect.perform (Print_endline ("Hello " ^ name))
It is then possible to interpret our program afterward, using a construction similar to pattern matching, to give a specific meaning to each effect.
Currently, it should be noted that effect propagation is not tracked by the type system. However, this is an experimental feature, which is used extensively in the new version of YOCaml. I am aware that resources are being devoted to developing an efficient type system to track effect propagation!
In general, when I don’t care about controlling the program’s flow, or I don’t need to add effects after the fact, I use modules. But in the case of YOCaml, the new effect system was leveraged to introduce effects dedicated to unit testing, allowing, for example, the mocking of time passing.
Once again, it’s really difficult not to go on at length about user-defined effects, which are a brand-new and very exciting feature of the language. I’ll conclude by simply sharing two articles written by Arthur Wendling that explain the use of effects in a very pedagogical way, along with a comprehensive bibliography on the literature related to effect abstraction in functional programming:
- Scopes and effect handlers
- Roguelike with effect handlers
- Effect bibliography It’s worth noting that this inversion/injection could also be done using records or objects. However, my experience with OCaml suggests that approaches using modules or effects (when you want to manipulate the program’s control flow) are often more straightforward and easier to reason about.
Regarding the future
OCaml is a constantly evolving language that changes with each version. In the section on dependency inversion, I briefly mentioned the recent inclusion of effects in the language to describe a multi-core runtime, reflecting the ongoing evolution of OCaml over the years. One can also note the integration of binding operators, which make the use of the triad Functors, Applicative Functors, and Monads more convenient — similar to computation expressions in F#.
Currently, many very exciting projects are underway to further improve the language:
A deep work on the expression of effects, with a newly added syntax, and a collection of research on the separation between operations and effects and, of course, on the propagation of effects in the type system.
Jane Street proposed a non-intrusive resource management model, inspired by Rust, introducing modalities and a bit of linearity.
A genuine foundational work has been initiated on the module language, making the implementation of Modular Implicits more smoothly achievable. We can also note the development of a hygienic macro system, the gradual integration of a staged metaprogramming system, and the implementation of an optimization back-end, reflecting OCaml’s strong activity in the innovation sector and making its development in the coming years very motivating and exciting!
Weaknesses
Even though I’m convinced that OCaml is an excellent language, claiming it is perfect would probably be disingenuous — after all, nothing is perfect. Here are, in my opinion, a few points that cast a shadow on OCaml as a language:
Lack of ad-hoc polymorphism. Although it is possible to work around it, for example using local module openings, the absence of ad-hoc polymorphism (via type classes — as in Haskell, or traits/implicit objects — as in Rust and Scala, or canonical structures — as in Coq) can sometimes make certain situations tricky. Even though I tend to prefer explicit relationships, over the years I’ve found several cases where this absence can be problematic:
The inability to describe type parameter constraints on polymorphic functions, leading to polymorphic equality and comparison functions in the standard library, which has caused much debate and, for example, required specialized versions of arithmetic operators for different numeric representations (int, int64, float).
Risk of combinatorial explosion when describing many relationships between modules. This is why the Preface library proposes a somewhat complex modular decomposition. However, even though the arrival of implicit modules is probably not in the short-term roadmap, recent work on the module language, as discussed in the “future of OCaml” section, is promising.
Cumbersome interaction between the module language and the value language. The module language is a different language with its own type system. Whether this counts as a weakness is debatable, but this distinction can be intimidating. It comes from the fact that OCaml’s module system was a pioneer in module theory and predates more recent innovations (e.g., 1ML). In practice, besides being complex to grasp, certain parts of the language are hard to specify correctly, for example recursive modules.
A language comfortable for functional programming, but impure. While I consider impurity a feature, importing idioms from purely functional languages (e.g., Haskell) can cause difficulties related to type inference, such as the value restriction. Even though OCaml has relaxed this restriction, its implications on polymorphic function inference can still be intimidating — for very good reasons.
- Syntax. Personally, I really like OCaml’s syntax and believe syntax should rarely be a major issue, but some choices can be confusing. For instance, type parameters prefix the type name: a list of
ais written'a list. Many of these choices aim to reduce syntactic ambiguity, and you get used to them quickly. However, coming from another language, some of these conventions may seem surprising. I think these weaknesses are generally debatable (because they are often justified), but I completely understand that they can be unsettling. However, I believe they are not enough to make OCaml unusable and should not be a major barrier to getting started with OCaml! The benefit of having an improvable language is that it constantly offers a range of potential improvements, motivating work that can also benefit other languages. And, to be entirely honest, being aware of these rough edges, I’ve more often found myself frustrated by the absence of language features that exist in OCaml in other languages, rather than complaining about these rough edges while writing OCaml itself. For these rough edges, there are usually workarounds (sometimes only partially satisfying, I admit) that allow one to work calmly and effectively.
To conclude on language
I have, in very broad strokes, outlined reasons why, in my opinion, learning OCaml is a very relevant choice. This language allows one to fundamentally understand certain very popular programming idioms (often poorly defined). Moreover, some aspects of the language perfectly serve industrial purposes, making good practices sometimes trivial to express! Much of this appeal can be experimented with in other languages, but OCaml’s strongly multi-paradigm nature allows one to centralize this learning in a single language. To my knowledge, in the jungle of partially popular languages, only Scala seems to cover as many topics, although, from my point of view, its object model is, essentially for interoperability with other JVM languages, far less interesting.
Since the goal of this article is not to be a tutorial, I deliberately skimmed over certain concepts, modules and effects. I hardly mentioned objects, polymorphic variants, or generalized algebraic types. If these topics interest you, I encourage you to read in detail the excellent Using, Understanding, and Unraveling The OCaml Language by Didier Rémy, along with the books I presented in the introduction, which is a goldmine for anyone wishing to deepen their knowledge of OCaml.
In conclusion, OCaml offers a diverse and rich set of language-level tools for learning programming, building industrial-grade programs that follow standards, as well as implementing complex data structures and category-theory-based abstractions such as a functional core, imperative traits, a rich and expressive inferred type system (allowing the expression of algebraic types and facilitating clear domain modeling), a module system for abstraction, reusability, and defining compilation units, an object model, the ability to express effects that can be propagated and interpreted a posteriori, and other advanced features. Even just to grasp advanced programming concepts, OCaml is an excellent candidate — which is why OCaml has been an obvious inspiration for many more recent languages, with Rust being a notable example.
OCaml as an ecosystem
Having an expressive language is very beneficial for building things (the phrasing is deliberately naive). However, in different contexts, both professional and personal, this is not enough:
In a professional context, it is obvious that if I want my team and I to be productive, it is probably not very relevant to have to build a whole tool stack before being able to start addressing the problem we are tasked with.
In a personal context, even though one could argue that building your technology stack is very educational, it changes the set of skills you actually want to develop. If, to build a small web application to get started with OCaml as a web language, I have to build my entire HTTP stack, it is very likely that OCaml is not the right choice. Rest assured, however, that OCaml has a rich tooling ecosystem for building web applications! That’s why the features offered by the language are not a sufficient metric to describe its viability for building and maintaining projects. The ecosystem is also a very important factor. It is for these reasons that .NET and the JVM, through relatively less expressive (but improving) languages like Java and C#, are also so popular. To assess the relevance of an ecosystem, I think it is important to consider several criteria:
The relevance of the runtime (or compilation targets) for the project. It’s likely that I wouldn’t recommend OCaml for embedding in a tiny, exotic hardware — though, knowing nothing about low-level programming (because it’s not my field at all), I could be wrong.
Its platform. Is its entire toolchain complete and ergonomic? From my point of view, this includes a package manager, a build system, good editor support (agnostic as possible), a solid documentation generator, and a collection of additional tools, such as a formatter (and many others).
The relevance of the available libraries (and their level of maintenance and discoverability, which generally implies having a package manager) with particular consideration for their ergonomics. For example, if I don’t have any cryptography primitives, I probably wouldn’t choose this technology to build a blockchain. There is a whole class of problems that are very difficult to solve in isolation or in a professional context. In this section, we will try to overview these different points to see if the OCaml ecosystem lives up to the language. I want to clarify that I am somewhat biased because I have been convinced of OCaml’s relevance since 2012, back when the ecosystem was drastically poorer. At that time, I tried to build projects by patching the gaps, which probably created a survivorship bias. Nowadays, thanks in part to industrial users, the OCaml ecosystem is much richer and more extensive, making it much easier to defend, although when some gaps still exist, the bad faith of the old user can resurface.
Compilation, runtimes, and additional targets
Since its inception, OCaml has had two compilation targets:
Native compilation, which produces highly efficient executables compiled for a specific architecture (and supports a large number of architectures). Moreover, whereas Windows was historically largely neglected, a special effort has been made to support it (also note the DkMl project, an independent initiative).
Compilation to bytecode (for a virtual machine), producing portable executables. The presence of a virtual machine enabled the development of the venerable Js_of_OCaml, which allows transforming OCaml bytecode into JavaScript, making OCaml perfectly viable for developing applications in the browser as well as in the Node runtime, and it is extensively used for this website. Using a similar approach, WebAssembly support was made possible very recently through the Wasm_of_OCaml project. Supporting compilation to WASM for a language with a garbage collector was a serious challenge, but with the recent specification of interaction between WASM and garbage collectors, OCaml now has perfectly decent WebAssembly compilation (and many ambitious web projects, like Ocsigen, are beginning to support WASM natively).
Moreover, the Melange project (historically BuckleScript) offers a way to transpile — mapping the OCaml AST to the JavaScript AST — as an alternative for producing JavaScript. If I were to compare Js_of_OCaml and [Melange](https://mela