Every morning, I wake up and ask myself: why isn’t OCaml more
popular? I mean, the language is not perfect, but the more I use it the
more I feel like this old language had it all figured out, somehow. I
mean, not in the literal sense: you write String.of_X
instead of String.from_X because the language has French
origins. But it is perfect in the sense that it has everything that is
important to me, except popular adoption. OCaml has its quirks, its old
age, but at the same time there is so much I appreciate about it.
I have some experience building amateur and professional software, in
many different languages, and as a result I have collected a list of
characteristics that I’ve come to appreciate over time. I think my
journey into programming is different from many. I learned and adored
functional programming before working in the industry, and while not my
very first language, Haskell was important to me early on. Functional
programming has allowed me to break big, complex problems down into
subproblems that I know how to solve. It has made me a better thinker.
Add to that the static guarantees of Haskell, which makes the mental
overhead much lower than for other languages, and I am also more
productive. Most importantly, with Haskell, I can focus more on the fun
parts of programming.
However, Haskell’s issues lie in its immense complexity and slow
compile times. I did say that Haskell lowers the mental overhead of
programming, but that’s only if you use a small subset of the language.
However because much of the community is very maximalist, introducing a
lot of complexity into the code becomes inevitable. Much of the code you
interact with is too “smart”, and becomes very hard to grok. Its runtime
is also very complex, and there’s always the chance of running into
notorious “space leaks”, that are extremely hard to debug.
At some point, I started exploring a language that is probably the
polar opposite to Haskell: Go. With Go, I learned to appreciate
simplicity and low-levels of abstractions, a good set of tooling, fast
performance and fast compilation speed. I also started to appreciate
good documentation that is easily available offline. The culture around
Go also places a lot of value on simple solutions, which made
interacting with the ecosystem easier. I can jump into any code base and
understand what is happening.
Over time, I also grew to hate the issues that come
with the language being so conservative: it is verbose in its error
handling yet manages to be fragile. At the same time, it doesn’t
have explicit null checks. These factors combined makes Go quite unpleasant and
easy to write buggy code in. I also found myself missing a REPL or fast
way of interacting with the program. The language is “predictably
disappointing”, which I guess is a good thing. However, the solutions to
those disappointments have been around before the language was even
created and I am just left feeling that these solutions could have been
implemented, and the compiler would not be much more complex as a
result. It just genuinely felt like the Go language designers didn’t
want to engage with any of the ideas coming from functional programming.
But I digress.
From these experiences, a list of features I consider to be “good”
features in a general programming language started to emerge:
- Fast compile times
- Fewer abstractions, and an easy to understand runtime
- Strong static guarantees
- Functional programming constructs. Especially pattern matching and
sum types. - Good performance
- Good documentation
And then, enter OCaml. This language just checks so many boxes:
- Strong static guarantees. Sum types, polymorphic variants, pattern
matching. - Simpler runtime semantics. OCaml is weird in that it’s a garbage
collected language for systems, similar to Go. A seasoned OCaml
programmer is probably able to gauge what the compiled assembler looks
like just by looking at the code. That is a great quality. - Fast compile times. When using Haskell or Rust, the slow compile
times really kill your productivity. With Dune, OCaml compiles extremely
fast. Faster than Go I believe? - Similar to Go, OCaml can be compiled statically into a single
binary, making it easy to deploy. - Great documentation. OCaml has odig to browse documentation offline,
utop as a REPL to explore code, and also separates interface files from
implementation files. This makes it very pleasant to browse OCaml code.
The types help guide the user. - Automatically inferred types. I didn’t think this would matter, as
writing out the types used to help my thinking. But now that I am more
“fluent”, it is more typing and less code. In OCaml, modules
separate interface files from implementation, and these are a better
place to put types.
I’m left feeling that the authors of OCaml have good taste. It
is an old language, and there are a few features that could probably be left out
like the OOP-related features, and some libraries in the ecosystem over-complicate things
like in Haskell. But overall, it’s just damn good. There are a lot of
other features I appreciate about OCaml that I didn’t share. But to
summarize why I love it: the right balance between simple and
expressive, good documentation and good tooling.
