Much ado about Monads

November 17, 2018

This is a minimal tutorial on Monads i.e. the mechanics rather than the philosophy. I’m not going to convince you that they’re important or beautiful or magical or powerful. On the other hand I will try to convince you that they are simple, that they’re not complicated, abstruse, or impenetrable. All you have to do is unwrap all of the indirection.

Also the explanation will be morally correct rather than formally/technically correct. So while the code does type check and compile (at least as of GHC 8.4.3) and I think I haven’t violated any of the Monad laws, these are not the actual definitions/implementations. Consequently I’m not going to cover Monad transformers.

Strong recommendation

Reduce all expressions to their most basic forms by hand (maybe on paper) in order to really understand what’s going on. That means transform each use of >>= and return into their definition in the Monad.

Note you need to include

import Prelude hiding (Monad(..))

at the top of your script in order to shadow default implementations if you plan to follow along.

Dictionary

The only syntax/concepts you need to understand

syntax definition parallel
foo :: Int Int type value foo Types in any statically type language
foo :: Bar Int Algebraic data type Bar composed with type Int no clue (this is algebraic data types)
foo :: Int -> String Type signature of function foo from type Int to type String Types in any statically type language
foo :: Int -> Int -> String Type signature of two argument function foo from type Int, Int to type String Types in any statically type language
foo :: a -> b Type signature of generic foo from any type a to any type b Types in any statically type language with generics
class Foo m where Parametric type class Foo with type parameter m public interface Foo in Java
class Baz m => Foo m where Parametric type class Foo with type parameter m with bounded type m public class Moo implements Foo, Baz in Java
(+) “prefixification” of an infix operator + no clue
instance Foo Bar where Instantiation of type class Foo by type Bar public class Bar implements Foo
instance Foo (Bar e) where Instantiation of type class Foo by parametric type Bar public class Bar<E> implements Foo
instance Baz e => Foo (Bar e) where Instantiation of type class Foo by parametric type Bar with bounded type e public class Bar<e extends Bar> implements Foo
\x -> 2*x Lambda function with parameter x lambda x: 2*x in python
f x f applied to x f(x)
f $ g x f applied to g applied to x (saves parentheses in f (g x)) f(g(x))
f . g $ x f composed with g f(g(x))
data Env = Env { foo :: String, bar :: Int} deriving (Show) Record Structs in C++ with default cout << representation (note that Env on the left side of = is different from Env on the right - left is type, right is the constructor [and they could be named distinctly] )

You also need to understand algebraic data types but I’m not going to explain that.

A utilitarian definition of Monads

A Monad is a type class whose instances implement two functions/operators: >>= and return (note return is a function not a keyword) which themselves abide by the Monad laws (though you should skip reading these until you finish this post).

class Monad mon where
  (>>=)       :: m a -> (a -> m b) -> m b 
  return      :: a -> m a

>>= takes a value of type m a (i.e. a value of the type that implements the interface) on the left and a function with signature k :: a -> m b on the right and then in toto returns a new value of the type m b. The function k :: a -> m b takes a value of type a and returns a value of type m b. You can think of this function k as a callback.

return takes a value of type a and returns a type of m a.

Keep in mind that each Monad instance stands or falls on its own merits and that the only thing in common between all of them is that they implement these functions.

Maybe

The simplest Monad to reason about is Maybe:


instance Monad Maybe where
    m >>= k = case m of
        (Just x) -> k x
        Nothing -> Nothing

    return = Just

What does this do? If m is the Just variant of Maybe then >>= just passes the wrapped value x on to the callback k. Otherwise (if m is the Nothing variant) >>= doesn’t pass anything and doesn’t even call the callback.

In a phrase: Monad Maybe is for sequences of functions each of which can fail.

An illustration:

trivial :: Maybe Int -> Maybe Int
trivial monkeyWrench =
    Just 3 >>= \a -> 
    monkeyWrench >>= \b -> 
    return $ a + b

obvSuccess = trivial $ Maybe 5
-- obvSuccess == Just 8

obvFail = trivial $ Nothing
-- obvFail == Nothing

The heuristic way to think about the composition Just 3 >>= \a -> is that the “inner” value in the Just is bound to the a.

I’ll point out that this would be useful, for example, in python where, if you scrape using BeautifulSoup, you end up writing things like

from bs4 import BeautifulSoup
soup = BeautifulSoup(html_doc, 'html.parser')
 
maybe_div = soup.find('div')

if maybe_div is not None:
    maybe_tr = maybe_div.find('tr')
    if maybe_tr is not None:
        maybe_td = maybe_tr.find('td')
        if ...

List

The List Monad instance “models” multiple results.

instance Monad [] where
    m >>= f = concat (map f m)
    return x = [x]
m :: [Int]
m = [1, 2, 3, 4]

f :: Int -> [Int]
f = \x -> [x, 2*x]

sameAndDouble = m >>= f
-- [1,2,2,4,3,6]

Not the most useful thing in the world with one application but with two applications you can, for example, compute cartesian product:

cartProduct = [1,2,3] >>= \x -> 
              [4,5,6] >>= \y -> 
              return (x,y)
-- [(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]

Either

Either is basically exactly the same as Maybe.

import Data.Either

instance Monad (Either e) where
    m >>= k = case m of
        (Right x) -> k x
        (Left e) -> Left e
    
    return = Right

Only difference is that Either is simply more useful (in that it passes on a message when it short-circuits).

trivialEither :: Either String Int -> Either String Int
trivialEither monkeyWrench =
    Right 3 >>= \a -> 
    monkeyWrench >>= \b -> 
    return $ a + b

obvSuccess = trivialEither $ Right 5
-- obvSuccess == Right 8

obvFail = trivialEither $ Left "nbd"
-- obvFail == Left "nbd"

Writer

Writer is the first of the slightly more advanced Monads.

The first thing to notice is that the type that instantiates the Monad is ((,) w). The type here is the tuple type construtor partially applied i.e. (w,). Meaning for a particular w (that itself instantiates Monoid [a Monoid is any type that implements mappend and mempty, mappend combines values of the type and mempty is the unit of the type, like 0 for integers]) and arbitrary a the type (w, a) is a Monad according to the following implementations of >>= and return:

instance Monoid w => Monad ((,) w) where
    return a = (mempty, a)
    m >>= k =
        let (w, a)  = m
            (w', b) = k a
        in (w `mappend` w', b)

All >>= does is combine the w value from m with the w' value produced by the callback.

A simple use case of Writer is logging: the first entry w in the tuple (w,a) serves as the log and the second entry a serves as the result of some operation. To make adding to the log ergonomic there’s a utility method tell

tell :: w -> (w, ())
tell w = (w, ())

Here’s an example:

type MyWriter = ([Int], String)

example :: MyWriter
example =
    tell [1,2,3] >>= \_ ->
    tell [3,4,5] >>= \_ ->
    return "foo"

example logs the the arrays [1,2,3] and [3,4,5] as a side-effect of producing the value "foo"

Reader

Reader is in a way the opposite of Writer: where as Writer implements threading write only state through a sequence of function calls (think about it - you can only append to the log), Reader implements threading read only state through a sequence of function calls.

instance Monad ((->) r) where
    return a = (\_ -> a)
    m >>= k = \r -> (k (m r)) r

Again the type that’s instantiating Monad is a little strange: it’s the partially applied function type constructor (->). This means that for a fixed “input” type r and arbitrary “output” type a the type (r -> a) is a Monad according to the foregoing implementations of >>= and return.

What do >>= and return do? >>= passes r to m (which reads r and produces some value) but also passes r to the callback (as well as passing the result of m). return injects a value into the sequence of function calls while ignoring r.

The use case is reading from some kind of environment object. Imagine needing to access some kind of environment/configuration object in a sequence of function calls and being unable to store that environment/configuration in global scope. What would you do? You would simply pass it as a trailing argument to each of the functions. This is exactly what Reader does.

In order to make accessing the environment/configuration object easier there are some utility methods:

-- just returns the environment
ask :: a -> a
ask = id

-- transforms the env (usually used to access member in record)
asks :: (r -> a) -> (r -> a)
asks f = f

-- Executes a computation in a modified environment. 
local :: (r -> r) -- function to modify env
      -> (r -> a) -- reader to run in the modified env
      -> (r -> a)
local f m = m . f

which are used as such

data MyContext = MyContext
    { foo :: String
    , bar :: Int
    } deriving (Show)

updateContext :: MyContext -> MyContext
updateContext m = m { foo = "foo" }

computation :: MyContext -> (Maybe String)
computation = asks bar >>= \n -> 
    local updateContext (asks foo) >>= \x -> 
    if n > 0 then 
        return (Just x) 
    else 
        return Nothing

ex1 :: Maybe String
ex1 = computation $ MyContext "hello" 1

ex2 :: Maybe String
ex2 = computation $ MyContext "haskell" 0

State

State is essentially both Writer and Reader except the state is now mutable and honestly would probably be better called StateTransformer (but that name is already taken by StateT, the state monad transformer). Unfortunately (due to compiler limitations) expressing State is a little messier than the preceding Monads insofar as it requires newtype. This just a thin wrapper and can be unwrapped using pattern-matching/destructuring. If it helps (it did for me!) just pretend State doesn’t appear.

newtype State s a = State (s -> (a,s))

instance Monad (State s) where
    return a = State $ \s -> (a, s)

    sm >>= k = State $ \s ->
        let (State st) = sm
            (a, s') = st s
            (State st') = k a
        in st' s'

The type that is monadic is s -> (a,s) i.e. functions that transform state of type s and simultaneously produce results of type a.

>>= extracts the state transforming function st wrapped up in sm, applies it to the current state, calls the callback on the result in order to produce another state transformer st', and then applies that state transformer st' to the as of then current state s'. The point is that state is passed automatically between sm and the callback (just like environment is passed automatically by Reader). Note that >>= must produce a state transformer itself (wrapped up in the State constructor) and hence is implemented as a lambda.

return produces the identity state transformer that just passes the state through without mutation but decorates it with some value of type a.

In order to help read/write state there are a couple of utility functions:

-- get the current state
get :: State s s
get = State $ \s -> (s, s)

-- replace the current state
put :: s -> State s ()
put s = State $ \_ -> ((), s)

-- mutate the current state
modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)

Here’s a small example

test :: State Int Int
test = 
    put 3 >>= \_ ->
    modify (+1) >>= \_ ->
    get 

res = test 0

IO

Finally the biggest, baddest of them all: IO. Not much to say here because IO is actually not implemented in Haskell (i.e. it is implemented in the runtime rather than as a library). You can play with it in a clean interpreter session or in this same session if you

import qualified Prelude

instance Monad IO where
    m >>= k = m Prelude.>>= k
    return = Prelude.return

main :: IO ()
main =
  putStrLn "What is your name:" >>= \_ -> 
  getLine >>= \name -> 
  putStrLn name

Acknowledgements

Examples/insights pulled from

What I Wish I Knew When Learning Haskell

The Haskell Book

All the code

import           Prelude                 hiding ( Monad(..) )
import           Data.Either
import qualified Prelude

class  Monad m where
  -- | Sequentially compose two actions, passing any value produced
  -- by the first as an argument to the second.
  (>>=)       :: m a -> (a -> m b) -> m b
  return      :: a -> m a

instance Monad Maybe where
    (Just x) >>= k = k x
    Nothing  >>= k = Nothing

    return = Just

desugared2 :: Maybe Int -> Maybe Int
desugared2 monkeyWrench =
  Just 3 >>= \a -> 
  monkeyWrench >>= \b -> 
  return $ a + b


instance Monad [] where
  m >>= f   =  concat (map f m)
  return x  =  [x]

m :: [Int]
m = [1, 2, 3, 4]

f :: Int -> [Int]
f = \x -> [1, 0]

demo = m >>= f

instance Monad (Either e) where
  return = Right
  Right m >>= k = k m
  Left e  >>= _ = Left e


demo1 = Left "boom" >>= \x -> return (x + 1)
demo2 = Right 100 >>= \x -> Left "no way!"

instance Monad ((->) r) where
  return a = (\_ -> a)
  m >>= k = \r -> (k (m r)) r

-- just returns the environment
ask :: a -> a
ask = id

-- transforms the env (usually used to access member in record)
asks :: (r -> a) -> (r -> a)
asks f = f

-- Executes a computation in a modified environment. 
local
  :: (r -> r) -- function to modify env
  -> (r -> a)  -- reader to run in the modified env
  -> (r -> a)
local f m = m . f


-- example

data MyContext = MyContext
  { foo :: String
  , bar :: Int
  } deriving (Show)

updateContext :: MyContext -> MyContext
updateContext m = m { foo = "foo" }

computation :: MyContext -> (Maybe String)
computation = asks bar >>= \n -> 
              local updateContext (asks foo) >>= \x -> 
              if n > 0 then 
                return (Just x) 
              else 
                return Nothing

ex1 :: Maybe String
ex1 = computation $ MyContext "hello" 1

ex2 :: Maybe String
ex2 = computation $ MyContext "haskell" 0






instance Monoid w => Monad ((,) w) where
    return a = (mempty, a)
    m >>= k =
        let (w, a)  = m
            (w', b) = k a
        in (w `mappend` w', b)

tell :: w -> (w, ())
tell w = (w, ())

type MyWriter = ([Int], String)

example :: MyWriter
example = do
  tell [1 .. 3]
  tell [3 .. 5]
  return "foo"

output :: ([Int], String)
output = example

newtype State s a = State (s -> (a,s))

instance Monad (State s) where
  return a = State $ \s -> (a, s)

  sm >>= k = State $ \s ->
    let (State st) = sm
        (a, s') = st s
        (State st') = k a
    in st' s'

get :: State s s
get = State $ \s -> (s, s)

put :: s -> State s ()
put s = State $ \_ -> ((), s)

modify :: (s -> s) -> State s ()
modify f = get >>= \x -> put (f x)

instance Monad IO where
  m >>= k = m Prelude.>>= k
  return = Prelude.return

main :: IO ()
main =
  putStrLn "What is your name:" >>= \_ -> 
  getLine >>= \name -> 
  putStrLn name