A word on monads

cover-image

A clever guy once said that once you understand what a monad is you lose the ability to explain what it is to other people - and he's (almost) right. Monads are a weird and wonderful thing, but sometimes are easiest to understand when given an example of one.

I'm going to try and do this by example, so to set the scene - lets use something like the following data structure:

    data Person = Person {
        name :: String,
        mother :: Maybe Person,
        father :: Maybe Person
    } deriving (Show)

So it's super simple. A Person has a name, and optionally has a mother and/or father, specified by the type Maybe Person for each.

Ok so lets get some test data:

    gemma = Person {
       name = "Gemma",
       mother = Just Person {
          name = "Michelle",
          mother = Just Person { 
             name = "Sandy", 
             mother = Nothing, 
             father = Nothing 
          },
          father = Just Person { 
             name = "Tim", 
             mother = Nothing, 
             father = Nothing 
          }
       },
       father = Just Person {
          name = "Tom",
          mother = Just Person { 
             name = "Kate", 
             mother = Nothing, 
             father = Nothing 
          },
          father = Just Person { 
             name = "Bob", 
             mother = Nothing, 
             father = Nothing 
          }
       }
    }

Sorry that uber terse, but it's simply:

                    Sandy
                   /
         Michelle <
       /           \
Gemma <      Kate   Tim  
       \    /
       Tom <
            \
             Bob

Next lets write a function which finds the the maternal grandparent (i.e. the mother of the mother):

    maternalgrandmother :: Person -> Maybe Person
    maternalgrandmother p =
       case mother p of
          Nothing -> Nothing
          Just mom ->
             case mother mom of
                Nothing -> Nothing
                Just granny -> Just granny

The above is the simplest implementation, and simply solves the question by asking the following questions:
1. Does the person have a mother?
2a. If not, then return Nothing.
2b. If so, then does the mother have a mother?
3a. If not, then return Nothing.
3b. If so, then return that person (the granny).

And of course this would go on and on if we were looking for the grandgrandmother, and grandgrandgrandmother etc. So this is the sort of repetition where a monad might be useful. So in the above example, let me highlight the crap that we'd like to abstract out:

    maternalgrandmother :: Person -> Maybe Person
    maternalgrandmother p =
       case mother p of
          Nothing -> Nothing
          Just mom ->
             case mother mom of
                Nothing -> Nothing
                Just granny -> Just granny

I.e. we just want the mother of the passed in person, and then the mother of that person - any interim value that happens to be Nothing should return Nothing back to the caller. Simple. So at best we want something like this:

    maternalgrandmother :: Person -> Maybe Person
    maternalgrandmother p = mother $ mother p

        -- with the caveat that it should return Nothing
        -- when either 'p' is Nothing, or 'mother p' is
        -- Nothing. 'mother mother p' may be Nothing too
        -- but thats ok, because we return a Maybe Person
        -- so all good there

So this is basically our ideal implementation, but we're missing the mechanism to implement the 'return Nothing when interim results are Nothing' bit. It's captured nicely in the comment, but the compiler unfortunately isn't that clever. So this is where MONADS come in!

The best sorta one-liner explanation of MONADS I could give is that they attempt to abstract the mechanism of computation of expressions in a particular context. I.e. in imperative languages, statements and expressions are computed in order, top to bottom. Obviously you have flow-control constructs to guide the computation in one way or another, but implictly the program will execute the statement on the next line once it's done with the current statement. This is a given.

HOWEVER - in a functional context (i.e. in haskell) - this 'one statement after the next computation strategy' is _NOT_ a given, and infact the decision of HOW to compute, and the ORDER that computation occurs is the responsibility of the current MONAD in play.

So without going into how to actually define your own MONAD, lets rather just look at some existing ones and how they are used. For the example above, the perfect solution is called the MAYBE MONAD (surprise!). And it simply says keep evaluating functions bound into the monad until a function evals to Nothing then return Nothing (stop evalutating subsequent bound functions), otherwise return the evalation of the final bound function.

    maternalgrandmother2 :: Person -> Maybe Person
    maternalgrandmother2 p =
       mother p >>=
          (\m -> mother m >>=
             (\g -> return (g)))