I said earlier that functional programming languages do not permit side effects, and used I/O as an example of a side effect. Astute readers will have asked themselves what the point of a language that can’t do I/O is. The answer, of course, is that there is very little point.
That’s not entirely true. You may, for example, drive a pure functional language from another language, so you can do all of the implicit parallelism tricks in the purely functional part and just call into it and get results from another part. Or you can use a language that sacrifices some of the ability
Languages like Haskell provide a mechanism in the language for collecting side effects. A monad is simply a sequence of computational steps. You use them in functional programming languages to collect sequential actions from inherently non-sequential code.
The canonical example of a monad is the IO monad, which collects input and output operations, but their potential scope is far greater. For example, you can use monads to implement software transactional memoryevery operation accessing memory is collected and then either applied simultaneously or rejected. Transactional memory is a very useful mechanism for implementing concurrency. It’s used in systems like GemStone Smalltalk, which allows web applications to scale up to thousands of nodesa server attempts to process a request, generates a set of changes to global state, and if it can apply them without the system entering an inconsistent state, then it does so. Otherwise, it simply discards them and tries again.
Monads are, in my mind, the single thing that makes functional programming worth learning. They provide an elegant bridge between the purely functional world, where things can happen in any order and the compiler is free to perform complex transformations on the code, and the real world where sequential operations matter.