A common statement about monads is that they make pure programming with side effects possible - so let us look at a first example of functions that allow to have debugging informations.
As a simple example I will only consider functions that have signature
f,g :: Double -> Double
then it is no problem at all to do calculations like
If I want to include debug info, in a pure language, I have to augment the above
signature to something like f',g' :: Double -> (Double, String)
in order to get
an acompanying debug message.
f' x = (f x, "f has been called: "++show (f x) )
g' x = (g x, "g has been called: "++show (g x) )
But now we end up with a crappy composition for
- We want to be able to collect the debug messages with every function call and
- It is now not obvious how to compose
f'
withg'
with the dot-operator
So let us define a new function composition and let us denote it (•)
the
all-knowing eye sees all debug messages, but to keep it simple we first try to
solve the task of verarbeiting a (value,message)-pair with a function that only
takes a single Double
argument.
This operation of f' >== (x,msg)
should be implemented quite easily, in a
first step we want to produce the value and message of f'
and then 'compose'
the debug messages.
And indeed:
(x,msg) >>= f' = let (fx,fmsg) = f' x
...
in (fx, msg++"\n"++fmsg)
So the next challenge is to write down the (•)
all-knowing eye operator
(g' • f') x = let (fx , fmsg) = f'x
(gfx, gmsg) = g fx
in (gfx, fmsg++"\n"++gmsg)
or equivalently but shorter
(g' • f') x = f' x >>= g'
Another nice feature would be if we had a function that 'lifted' a given double kind of automatically in the debuggable interface - for historical reasons this function has many names unit/pure/return, which reflect the various settings in which monads came up and sometimes only make sense in the respective situations.
In this situation I'd like to call this function inject.
inject :: Double -> (Double, String)
inject x = (x, "")
Going back to our initial setting, note that f
and g
have both been
functions that neither produced nor recieved (val, msg)-pairs, so we'd like to
have a formalism to make an f'
given f
, or in other words lift f
into the
monad. Thus we define
lift :: (Double -> Double) -> Double -> (Double, String)
(lift f) x = (f x, "")
or more abstractly
lift f = inject . f
At last in this first chapter we want to observe that every well behaved monad respects a few laws, that lead to certain simplifications or optimizations.
lift f • lift g = lift (f • g)
Another use-case for monads are functions that have more than one reasonable choice for results - Dan Piponi uses complex square roots as an example, but other examples could be a function that chooses which ice-cream flavour you want to have on your ice-cone. Everyone knows vanilla and chocolate go well with another, but chocolate and lemon is rarely a good coice. Another example would be musical chords - not all notes make a harmonic sound. And a last example I see is all possible moves in a game of chess or go.
Staying with Piponi we analyze complex roots of complex values, you might
remember that the equation