I've been using Haskell in production for 8 years.
I've been using OCaml in production for 8 months.
It's time to compare those two languages.
Syntax
Haskell probably has the most elegant syntax across all languages I've seen (maybe Idris is better because dependently typed code can become ugly in Haskell really quickly).
There's utter joy in expressing your ideas by typing as few characters as possible.
OCaml, being a language from the ML family is great too, but still, Haskell is more tacit.
Compare a few examples:
Sum of all numbers in a string
Using just the standard library
Haskell
-- strSum "100 -42 15" = 73
strSum :: String -> Int
strSum = sum . map read . words
OCaml
(* str_sum "100 -42 15" = 73 *)
let str_sum (str: string): int =
str
|> String.split_on_char ' '
|> List.filter_map int_of_string_opt
|> List.fold_left (+) 0
Defining a new binary tree type
Haskell
data Tree a
= Leaf
| Node a (Tree a) (Tree a)
OCaml
type 'a tree =
| Leaf
| Node of 'a * 'a tree * 'a tree
Parsing
Return the result on successful parsing of lines like the one
below where "Status" equals to zero and the result is an even number"Status: -1 | Result: 42"
Haskell
parseLine :: String -> Maybe Int
parseLine line = do
["Status:", "0", _, "Result:", result] <- Just $ words line
n <- readMaybe result
guard $ even n
pure n
OCaml
let parse_line (line: string): int option =
let ( let* ) = Option.bind in
let* result =
match String.split_on_char ' ' line with
| ["Status:"; "0"; _; "Result:"; result] -> Some result
| _ -> None
in
let* n = int_of_string_opt result in
if n mod 2 = 0 then Some n else None
The above are just a few random code snippets. They don't give an idea of all possible programs that could be written in those languages. But I hope they can quickly highlight the similarities and differences between the two languages.
This slowly leads us to the next point.
Features
Haskell has waaaaaay more features than probably any other programming language (well, C++ can compete). This is both good and bad.
It's good because you have the tools to solve your problems in the best way.
It's bad because you have those tools. They're distracting. Every time I need to solve a problem in Haskell, I'm immediately thinking about all the ways I can design the solution instead of, ahem, actually implementing this solution.
I'm interested in building stuff, not sitting near my pond on a warm summer day, thinking if TypeFamilies + DataKinds would be better than GADTs for making illegal states unrepresentable.
If I come to an existing OCaml project, the worst thing previous developers could do to it is have poor variable names, minimal documentation, and 200+ LOC functions. That's fine, nothing extraordinary, I can handle that.
If I come to an existing Haskell project, the worst thing previous developers could do... Well, my previous 8 years of Haskell experience can't prepare me for that 😅
That's why I feel more productive in OCaml.
I do miss some Haskell features at times. But I've seen their ugly side and what they can do to your output.
Consider the following table with a full comparison of major features.
Feature comparison table
Feature | OCaml | Haskell |
---|---|---|
Expression-oriented syntax | ✅ | ✅ |
Immutability by default | ✅ | ✅ |
Higher-Order Functions (HOFs) | ✅ | ✅ |
Anonymous functions (lambdas) | ✅ | ✅ |
Algebraic Data Types (ADTs) | ✅ | ✅ |
Pattern Matching | ✅ | ✅ |
Parametric Polymorphism | ✅ | ✅ |
Type Inference | ✅ | ✅ |
Monadic Syntax Sugar | ✅ | ✅ |
Garbage Collector | ✅ | ✅ |
Multithreading | ✅ | ✅ |
GADTs | ✅ | ✅ |
Purity by default | ❌ | ✅ |
Composable laziness | ❌ | ✅ |
Type classes | ❌ | ✅ |
Higher-Kinded Types | ❌ | ✅ |
Opt-in language features | ❌ | ✅ |
First-class modules | ✅ | ❌ |
Polymorphic variants | ✅ | ❌ |
Objects | ✅ | ❌ |
Classes and Inheritance | ✅ | ❌ |
Ergonomic mutability | ✅ | ❌ |
Ecosystem
Let's be honest, both programming languages are niche FP langs. So you shouldn't expect first-class support for the latest modern framework that just got published.
However, in my experience, despite needing to write more custom bindings, you have solutions for the majority of common tasks.
For example, in OCaml, you can find:
- otoml: A TOML parser
- Mint Tea: A TUI framework
- ocaml-opentelemetry: Instrumentation for OpenTelemetry
- awsm: OCaml AWS Client
- petrol: An OCaml SQL API made to go FAST
And so on. Similar story for Haskell.
I'd still say that the Haskell ecosystem has more packages and more ready-to-go solutions.
It's easy to show the difference in the following example.
Number of Stripe API client libraries:
- Haskell: 13
- OCaml: 1 (last change was 8 years ago, so it's more like zero)
You may find a solution in Haskell. But often you'll discover too many solutions, you won't know which one to choose.
Choosing a library in Haskell becomes a separate skill you need to master. Haskellers even blog their recommendations on how to choose a library! And you'll face this dilemma over and over again.
Often, a new Haskell library is created not because it solves a different problem.
But because the author wanted to write it differently (using different abstractions, playing with new features, etc. Who doesn't want a new streaming library based on LinearTypes???).
It's not exciting to write a GitHub API client and parse tons of JSON.
But it is exciting to design a logger with comonads.
Tooling
The Haskell tooling evokes the most controversial feelings. It's like an emotional roller coaster:
- 🤩 Hoogle is the best! I can search through the entire ecosystem by using just a type signature!!!
- 😨 Wait, why build tooling error messages are so bad, what do you mean it couldn't find a build plan for a working project???
- 🤩 Global content-addressable storage for all dependencies is such an amazing idea!!!
- 😨 What do you mean I need to recompile my IDE because I changed my package???
- 🤩 I can automatically test all the code snippets in my package docs!!!
- 😨 Wait, why the standard library doesn't have docs at all for this version I use???
And so on.
Using Haskell tooling is like always being in the quantum superposition of "How do you even use other PLs without such wholesome Haskell tools???" and "How Haskellers can live like that without these usability essentials???".
OCaml, on the other hand, hits differently. Because its ecosystem is smaller, you actually get surprised every time you find something working!
For example, the VSCode plugin for OCaml based on Language Server Protocol (LSP) works out-of-the-box. I never had any issues with it. It just works ™️
The ergonomics of starting with OCaml tooling might not be the best but they're straightforward and robust. And they work most of the time.
To get a full picture, refer to the following table for the full comparison of available tooling in both languages.
Tooling comparison table
Tool | OCaml | Haskell |
---|---|---|
Compiler | ocaml | ghc |
REPL | utop | ghci |
Build tool | dune | cabal, stack |
Package manager | opam | cabal |
Package repository | opam | Hackage |
Toolchain installer | - | ghcup |
Linter | zanuda | hlint |
Formatter | ocamlformat, topiary | fourmolu, stylish-haskell, hindent, ormolu |
Type Search | Sherlodoc | Hoogle |
Code search | Sherlocode | Hackage Search |
Online playground | TryOCaml | Haskell Playground |
LSP | ocaml-lsp | HLS |
Compiler messages
I want to highlight the compiler aspect of tooling separately since this is the tool you interact the most with.
Especially, compiler suggestions.
When using FP languages, the compiler is your best friend! You rely on it heavily to understand why your assumptions haven't been codified precisely.
Therefore, the compiler must present the information in the most accessible way.
In my view, Haskell compiler messages tend to be verbose with lots of contextual, often redundant, and distracting information.
OCaml compiler messages, on the other hand, are quite succinct. Sometimes too succinct.
Consider the following example.
Haskell: Compiler messages example
Program with an error
x = 1 + [3, 1, 2]
Compiler output
OCaml: Compiler messages example
Program with an error
let x = 1 + [3; 1; 2]
Compiler output
This is just one example (and most likely not the best one), but you can already see the differences in how information is presented and how types work in different languages.
Standard library
I believe the standard library deserves a separate mention too.
It shapes your first program in a language and guides you through all future journeys.
A great standard library is a cornerstone of your PL success.
A poor standard library is a cornerstone of never-ending bikesheds about a better standard library (including an endless variety of alternative competing standard libraries).
I'm a big proponent of the idea that a standard library should be batteries-included.
Give me an Option-like type, a UTF-8 string, Map and HashMap, JSON and XML parsers, async primitives, and so on, so I can avoid learning your poor implementation of dependency tracking and build tooling. (Build Systems a la Carte is a thorough analysis of the space of dependency trackers and build tools.).
Both Haskell and OCaml have kinda barebones standard libraries. They have minor differences (e.g. Haskell doesn't include Map and HashMap; OCaml doesn't have non-empty lists and Bitraversable). But overall they're similar in the spirit.
The Haskell standard library is called base
and OCaml standard library is called.. well, it's just "the standard library".
However, one difference is striking. The quality of Haskell documentation sometimes can amaze even a seasoned developer.
Haskell has a few more nice features, like the ability to jump to sources from docs but I've been told such features are being cooked for OCaml too 👀
Compare a few doc snippets for the List data type (one of the fundamental structures in FP):
Haskell
OCaml
You may argue that the result of such functions is obvious, therefore there's no need to write essays under each function.
I'm a fan of example-driven documentation, and I love seeing usage examples in docs! This immediately gives me an idea of how I can leverage the API in the best way.
Conclusion
I want to end this blog post by saying:
Both languages came a long way to support real industrial needs.
They're still small compared to mainstream languages.
If you're not critically dependent on the presence of some specific SDK, you can choose any and have lots of joy while coding your next app 🧡
However, I prefer OCaml nowadays because I feel that I can focus on actually building stuff with this language.
If you liked this blog post, consider following me on YouTube, X (formerly known as Twitter) or sponsoring my work on GitHub
Discussions
Besides the comment section below, you can also find the discussions of this blog post in various places:
- 𝕏 by @chshersh (210+ 🧡, 14+ comments)
- OCaml Discuss (16+ 🧡, 16+ comments)
- Haskell Discourse (27+ 🧡, 107+ comments)
- Lobste.rs: ml (39+ ⬆️, 16+ comments)
- Reddit: /r/ocaml (22+ ⬆️, 18+ comments)
- Reddit: /r/haskell (91+ ⬆️, 57+ comments)
Top comments (26)
Thanks! I hope you enjoyed your time at Bloomberg 🤗
Indeed, this particular example could be written better with the usage of pattern matching. I wanted to show the difference between guards and if-then-else. But looks like this particular example is not that convincing.
I'll change it to a better one when I come up with it!
Meanwhile, you might have some other example in mind?
If we are comparing succinctness, let me offer an alternative parsing example :-)
Good example! I often forget OCaml has
sscanf
too as well asprintf
😅I was so infuriated by that library that I made one to fix all its problems. ;)
github.com/dmbaturin/otoml/
It's actually compliant with the TOML standard, uses transparent types (but also offers helpers for getting nested values), and uses functors to allow the user to plug any datetime and bignum libraries or stick with built-in native int and string dates.
Well, and offers informative parse errors, too.
Hey @dmbaturin
I was informed that
ez_toml
is better thanotoml
:discuss.ocaml.org/t/8-months-of-oc...
Do you have a comparison with this library somewhere?
Thanks for letting me know! Your library looks great, I'll update the link 🔗
I follow you on twitter and saw your recent posts about ocaml, so I decided to actually try it out. Using it for a bit during the holidays made me feel like it's just worse Haskell. When I saw your post, I thought that I'd see some features that are amazing, while it might just be my viewpoint, the stuff you're showing me here makes me want to use Haskell even more.
Less than language features a few of the libraries are really cool tho. I'd love to have something akin to owl (cl.cam.ac.uk/~lw525/owl/chapter/pl...)
It's totally fine to have different preferences!
If you enjoy using Haskell, you can continue using it 🤗
You forgot the best asset OCaml which is its Web framework. Ocsigen is the most advanced web development framework. It's revolutionary once you get how it works. Nothing else can compete, in any language.
I never tried Ocsigen. I guess, it's hard to compare two languages fully because you can never try everything.
But you reply gave me some motivation to try it 👀
I’ve been using OCaml for 4 years now, but never used Ocsigen for web development. I have some experience with ReScript with React tho. I’m really curious about using OCaml for both front/backend; why would you recommend Ocsigen (vs something like Dream)
Awesome post!
I tried OCaml many years ago. I think around the same time I started playing with Haskell, but gave up on OCaml due the fact that Haskell felt more pleasant to use. You definitely made me wanna try OCaml again :)
I'm glad my post was inspiring enough 😊
I received tons of feedback on my OCaml vs Haskell blog post! Thanks a lot to all who read and shared their thoughts 🤗
Using the feedback, I improved my post by:
✍️ Changing the most triggering exponentiation example to a different one
✍️ Added links to all discussions of my blog post all around the Internet
✍️ Changed feature from 'Laziness by default' to 'Composable laziness'
✍️ Added 'topiary' as an OCaml formatter
✍️ Changed the suggested TOML library from 'To.ml' to 'otoml'
✍️ Changed the suggested AWS library from 'ocaml-aws' to 'awsm'
Hope you enjoy it!
Terrific post!
For the comparison table, possibly include records support. This has been a Haskell deficit for decades, and no thank you, lenses are a bloated, heavyweight bolt-on when you just want simple, lightweight record support.
Thank you!
Haskell kinda has a better record story nowadays. You can actually have nice records in Haskell without using lenses!
Nice article. It's great to have the two languages side by side as I'm tempted to have a closer look at OCaml.
Thanks!
Yes, I really wanted to compare these two languages.
You can find other blog posts comparing Haskell and OCaml (some are older, some are newer). But I wanted to provide my take anyway and share my experience 😌
Great article! Really nice to see you're enjoying OCaml, I've been looking at it too, but have not actually used it yet, but this makes me even more interested in trying it out.
Thanks a lot, Pablo!
If you liked Haskell, I think you'll enjoy OCaml too 🙂