WTF is a Monad?
An exploration in Clojure
Robert C. Martin
Object Mentor Inc.
Copyright 2010 by Robert C. Martin
All Rights Reserved.
Thursday, June 3, 2010 1
The Monads are Coming
to EAT your Brains!
Thursday, June 3, 2010 2
In Reality, Monads are Wimps
Thursday, June 3, 2010 3
In Reality, Monads are Wimps
Thursday, June 3, 2010 3
Upper and Lower
Mondads are adapters.
Given a lower form (e.g. integers)
and functions that manipulate it (e.g. +, -, *, /, etc.)
Given an upper form (e.g. a string of dots)
Where there is a mapping between the lower and upper forms
e.g. 3 maps to ... and vice-versa
Then a monad of the upper form allows data in the upper form to
be manipulated by functions of the lower form.
e.g. You can apply the monad to the + operation to create a new
function (dot-add) that takes upper form data.
applying dot-add to ... and .. yeilds .....
To summarize: A monad is an adapter between a lower form and
an upper form.
That is not _all_ monads are, but its a good first step.
Thursday, June 3, 2010 4
Dots
Thursday, June 3, 2010 5
Non-Monadic Dots in Ruby And Closure
def dotsToN(d) (defn dots-to-n [dots]
d.size() (count dots))
end
def nToDots(n) (defn n-to-dots [n]
"."*n (apply str
end (repeat n ".")))
def addDots(da, db) (defn add-dots [da db]
a = dotsToN(da) (let [a (dots-to-n da)
b = dotsToN(db) b (dots-to-n db)]
nToDots(a + b) (n-to-dots (+ a b))))
end
Thursday, June 3, 2010 6
Monadic Dots in Ruby And Closure
def dot_result(n) (defn dot-result [n]
"."*n (apply str (repeat n ".")))
end
def dot_bind(d, &f) (defn dot-bind [d f]
f.call(d.size()) (f (count d)))
end
def dot_add(da, db) (defn add-dots [da db]
dot_bind(da) do (dot-bind da
|a| dot_bind(db) do (fn [a] (dot-bind db
|b| dot_result(a+b) (fn [b] (dot-result
end (+ a b)))))))
end
end
Thursday, June 3, 2010 7
Series vs Parallel
In the first (non-monadic) example the conversions took
place in parallel.
(defn add-dots [da db]
(let [a (dots-to-n da)
b (dots-to-n db)]
(n-to-dots (+ a b))))
In the monadic example, the conversions took place in
series.
(defn add-dots [da db]
(dot-bind da
(fn [a] (dot-bind db
(fn [b] (dot-result
(+ a b)))))))
This will be important later, so keep it in mind.
Thursday, June 3, 2010 8
Bind and Result
Upper
Bind
Upper
Lower
Upper
Lower
Upper
f Result
Thursday, June 3, 2010 9
Monadic Dots. (ok, thats clear.)
da
Bind
ds
a db
a db
ds
Bind
ds
b
b Result
Thursday, June 3, 2010 10
Monads are inspectors
Since the conversion from lower to upper takes place in
series, the conversions have the ability to abort the
operation.
e.g. if there are values in the upper form that cannot be handled by
the lower form, then the series nature of the monad allows the
conversions to abort before the lower functions get called.
For example, lets say we have a lower form of real
number, and an upper form of complex number.
Our monad converts complex numbers to real and invokes real
operators.
But if the complex number has a non-zero imaginary component,
then the monad must abort, because the real-number functions
cant deal with imaginary numbers.
Thursday, June 3, 2010 11
Complex
Thursday, June 3, 2010 12
Nil checks.
Many functions cannot deal with nil arguments. They
throw NPEs.
So we define a lower form that cannot accept nil,
An upper form that can.
And a monad that adapts the two and aborts the operation if there
is a nil.
This is the Maybe-Monad.
Thursday, June 3, 2010 13
No Nil : Maybe Monad
Thursday, June 3, 2010 14
The real power of being in series.
Since the conversion takes place in series...
Each converter can determine how many times it is applied.
The maybe-monad applies one or zero times.
But what about 2, 3, 4?
Imagine two forms:
a lower form of number
An upper form of list of numbers.
What does [1 2 3] + [4 5 6] mean?
it could mean: [5 6 7 6 7 8 7 8 9]
In order for our monad to convert from a list to a number, it must
invoke the conversion n times.
Given two arguments in list form, the lower function will be called n*m
times.
This is the sequence monad.
Thursday, June 3, 2010 15
Lists: The Sequence Monad
Thursday, June 3, 2010 16
Probabilities
Lower form: An event.
Represented by a token: e.g. 3, bob, :tic.
Upper form: A probability distribution
Represented as a map: {:heads 1/2, :tails 1/2}
The monad
generates events from distributions,
applies functions to the events to generate other events.
generates new distributions from those events.
e.g. What is the probability distribution for rolling two dice?
use the distribution for one die.
use the monad to roll the die twice and generate the events.
add the two events together.
the monad will generate a new distribution.
Thursday, June 3, 2010 17
Dice: The Distribution Monad
Thursday, June 3, 2010 18
State
Functional programs are stateless.
How do you simulate state in stateless code?
You pass the state to each function and have it return the new
state.
You call the functions in series! because the output state of one is
the input state of the next.
Lower form: A function.
Upper form: A function that takes and returns state.
Result:
In the context of the monad our functions appear stateful.
Outside the monad we see that the operation was functional and
not stateful.
Thursday, June 3, 2010 19
Stateful Functionality
The state monad
Thursday, June 3, 2010 20
fin
unclebob @ objectmentor.com
fitnesse.org
cleancodeproject.com
Thursday, June 3, 2010 21