Monads Are the Foundation; Purity Is the Bedrock
I’ll assume that you know what monads are. If not—well, there are plenty of blog posts around on the internet explaining that they are like burritos, that they are simply monoids in the category of endofunctors, or that they’re like making it so you can override the semicolon operator (none of these explanation are very enlightening).
Monads are useful, right? If you have implemented a medium-size project in Haskell in your life, you know this as a concrete fact, it is in your bones. It’s natural that you want to export the usefulness of monads to other languages.
Monads became useful by accident. Haskell did not always use monads for IO. In A history of Haskell: being lazy with class (1997 ACM, Microsoft), we learn that early versions of Haskell used a “clumsy” I/O system which “was a source of considerable embarrassment”. The problem was that you can’t reliably control the sequencing of functions with side effects in a language with lazy evaluation.
In 1996, with Haskell version 1.3, monads enter the picture. Monads provide a modern, easy-to-use way to sequence side effects in a language with lazy evaluation.
Since then, we’ve seen the benefits of monads.
Monads let us reason about side effects in our programs.
We can see a function with a type signature like
String -> Int
and know that this function doesn’t perform any I/O or modify any global
We can write transactions for software transactional memory and know that
the transactions do not leak state from failed transactions, because the
STM monad cannot perform I/O.
You’d like to use monads in another language? Great! Unfortunately, most languages don’t enforce function purity, and allow you to execute arbitrary I/O in whatever function you like. This means that you can no longer use type signatures to reason about side effects. That’s the main point of monads, and it’s only possible in a language which enforces function purity to begin with.
This is one of Haskell’s biggest contribution to functional language research. Haskell needed monads because Haskell uses lazy evaluation, which drove researchers to look for a way to encode side effects in a pure functional system—a system which is purely functional at least stands a chance of working under lazy evaluation. However, lazy evaluation is not necessary to work with monads and enjoy their benefits, only functional purity is necessary.
Think about it this way: if monads are the foundation which you use to express side effects in your program, then functional purity is the bedrock underneath. If you try to use monads in impure languages, you’ll get leaks in your foundation.
What Haskell teaches us about monads is this: “functional purity has practical applications.”