Monads are hard because …

Here’s a nice quip from Luke Gorrie on Twitter:

Monads are hard because there are so many bad monad tutorials getting in the way of finally finding Wadler’s nice paper.

Here’s the paper by Philip Wadler that I expect Luke Gorrie had in mind: Monads for functional programming.

Here’s the key line from Wadler’s paper:

Pure functional languages have this advantage: all flow of data is made explicit. And this disadvantage: sometimes it is painfully explicit.

That’s the problem monads solve: they let you leave implicit some of the repetitive code otherwise required by functional programming. That simple but critical point left out of many monad tutorials.

Dan Piponi wrote a blog post You Could Have Invented Monads! (And Maybe You Already Have) very much in the spirit of Wadler’s paper. He starts with the example of adding logging to a set of functions. You could have every function return not just the return value that you’re interested in but also the updated state of the log. This quickly gets messy. For example, suppose your basic math functions write to an error log if you call them with illegal arguments. That means your square root function, for example, has to take as input not just a real number but also the state of the error log before it was called. Monads give you a way of effectively doing the same thing, but conceptually separating the code that takes square roots from the code that maintains the error log.

As for “so many bad monad tutorials,” see Brent Yorgey on the monad tutorial fallacy.

By the way, this post is not Yet Another Monad Tutorial. It’s simply an advertisement for the tutorials by Philip Wadler and Dan Piponi.

17 thoughts on “Monads are hard because …

  1. I still think many more people would understand monads better if one of the good tutorials used a language other than Haskell or Scala to teach the idea. There is room for a better understanding of the topic among people who don’t program in such languages, and understanding monads better might lower the psychological barrier to learning Haskell for some.

  2. One of the best ways to see if you understand something is to try to explain it to someone else. Once I had my monad Aha! moment I too came up with an analogy and figured out how I would use it as part of a monad tutorial.

    The problem with monad tutorials is that people don’t realize that the bulk of them are for the benefit of the writer, not any prospective reader.

  3. > I still think many more people would understand monads better if one of the good tutorials used a language other than Haskell or Scala to teach the idea.

    Monad requires rank-2 types. Not all programming languages have this – e.g. Java doesn’t.

  4. @Eugine,

    Trouble with attempts to demo monads in other languages is that all other languages already impose their own idea of effects, so the demo is continually fighting that, which makes it really hard to see the point. Google for monad in Java for some examples.

  5. Achilleas Margaritis

    Personally I stopped finding monads hard when I realized they are functional patterns that use the host language’s type system to enforce the pattern.

    Perhaps all monad tutorials shall start with ‘a monad is a way to enforce a specific pattern…’.

  6. You just haven’t seen my super monad tutorial to end all monad tutorials. It involves astronauts in space suits eating burritos filled with nuclear waste!

  7. This is a repost.

    @techtangents: I’ve been modelling my monads in C# with a class representing the higher order generic along with a combined class that has the three monad functions (pure/point/return, map, and bind/flatMap).

    eg.

    interface Monad<M> {
    IMonad<M, A> pure(a);
    }
    interface IMonad<M, A> where M : Monad<M> {
    IMonad<M, B> pure(B b);
    IMonad<M, B> map(Func<A, B> f);
    IMonad<M, B> bind(Func<A, IMonad<M, B>> f);
    }

    class Option : Monad<Option> { …}
    class OptionM<A> : IMonad<Option, A> { …}
    class Some<A> : OptionM<A> { … }
    class None<A> : OptionM<A> { … }

    I get the type safety of monads. I have some nesting problems because I don’t have a macro to deal with the repeated nested calls to map and flatMap that usually expand the code horizontally.

  8. If I were teaching monads in a class today I think I’d present them as a way to overload list comprehensions.

    Eg. see Wadler’s http://ncatlab.org/nlab/files/WadlerMonads.pdf and my own implementation in Python http://blog.sigfpe.com/2012/03/overloading-python-list-comprehension.html

    List comprehension is pretty familiar to people these days.

    > Achilleas Margaritis…
    > Perhaps all monad tutorials shall start with ‘a monad is a way to enforce a specific pattern…’.

    I think you have it the wrong way round. A so-called “pattern” is an attempt to implement a function or type class in a language that has poor support for such things. :-)

  9. Achilleas Margaritis

    I did not say that monads are patterns. I said monads are constructs that allow the enforcement of specific patterns.

  10. Monads clicked for me when I tried implementing the examples in Professor Wadler’s paper in Erlang. It’s not pretty but it’s just different enough from the Haskell be enlightening.

  11. > That’s the problem monads solve: they let you leave implicit some of the repetitive code otherwise required by functional programming.

    But that’s not a specialty of monads; generally higher-order functions are good at that. I think you may have come to the conclusion that monads do things implicitly because in Haskell there is special syntax for it (do-notation, where roughly speaking (>>=) is implicit).

    More mathematically (as you understand much better than me) a monad is a functor f with “injection” (return :: a -> f a) and “merging” (join :: f (f a) -> f a or equivalently the (>>=) or (>=>) functions) with some nice commuting properties. Fundamentally there isn’t anything more to it.

    Now why are monads abundant in programming? Computation often is “dirty”. It may have side effects (IO), it may possibly fail (Maybe, Either), it may be indeterministic (List), it may have state (State), it may be only symbolical (free monad), etc.

    But while we can not get rid of it, we often would like to reason without the dirt. For example, while there is only the dirty (readFile :: FilePath -> IO String) we would like to think (readFile :: FilePath -> String) . Now if you have a function which acts depending on the contents of a string, (act :: String -> IO ()) you would not be able to apply it to the return value of (readFile).

    Monads come to the rescue because they let you combine (a -> IO b) >=> (b -> IO c) :: (a -> IO c). There it is, composing dirty things!

  12. The linguistic barrier is the biggest problem. This should be a sufficient intro to Monads yes?

    // abstract class Monad
    abstract class FlatMappable {
    // return(a) | unit(a)
    constructor(a){ … }
    // bind(f) | >>= | *
    abstract flatMap(f);
    }

Comments are closed.