I recently ported the Hardcaml_step_testbench library, one of the libraries that we use at Jane Street for Hardcaml simulations, from using monads to using algebraic effects, a new OCaml 5 feature. This blog post walks through what algebraic effects are, why you should consider using them in lieu of monads, and how to actually work with them using the Handled_effect library. One thing I’ve come to believe is that most of what can be done with monads can be done with algebraic effects in a much more elegant way.
Algebraic effects were originally added to OCaml for general-purpose concurrent execution of programs for OCaml 5, which supports thread-level parallelism. The fact that they can be repurposed for Hardcaml simulations speaks to how well-thought-out and general a language feature this is.
I am writing this post as someone who is not a type-theory expert. The fact that I can use algebraic effects without fully understanding the underlying mechanics is one nice feature of their design.
(The library is still named Oxcaml_effect on github. We are in the process of renaming it to Handled_effect .)
What’s wrong with monads?
Monads have been used by OCaml programmers for a long time to model computation. Jane Street’s own monadic Async library, which is used for concurrent programming, powers a lot of our infrastructure. Why would we want to replace it?
Reason 1: Monads infect your code and make everything harder to read
Recall that monads have the following type signature:
(* This is part of the [Monad.S] module type *) type ' a t val return : ' a -> ' a t val bind : ' a t -> f :( ' a -> ' b t ) -> ' b t val map : ' a t -> f :( ' a -> ' b ) -> ' b t
Suppose we are interacting with a server that helps us accumulate numbers. Using the Deferred.t monad from Async as an example, an interface might look something like this:
... continue reading