Skip to content

Instantly share code, notes, and snippets.

@epsilonhalbe
Created May 29, 2014 15:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save epsilonhalbe/ee1b020c562424a47cfb to your computer and use it in GitHub Desktop.
Save epsilonhalbe/ee1b020c562424a47cfb to your computer and use it in GitHub Desktop.

Side Effects

Intro

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 $(g . f) x = g (f x)$

Modelling side effects

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

  1. We want to be able to collect the debug messages with every function call and
  2. It is now not obvious how to compose f' with g' 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.

One ring to >== them all

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

Sauron

(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'

Pure/Unit/Return

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, "")

Lift

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

Law and Order

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)

Multivalued Functions

Intro

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.

Modelling multivalue functions

Staying with Piponi we analyze complex roots of complex values, you might remember that the equation $x^2 = a$ has exactly two solutions for all $a\neq 0$ so $x^2 = 4$$x = 2$ or $x = -2$ so we could define

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment