
Functors, Applicatives, and Monads in Pictures (2013) - bradcomp
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
======
4ad
Nice pictures, but I just don't get it.

Sure, it's very easy for me to understand monads mathematically, what kind of
structure are they. I don't need any pictures for that, the definition
suffices for me.

But that doesn't tell me _what are they good for_. We invent things for a
reason, and I don't see the reason. Now I _know_ the reason, it's been told to
me many times (some way to wrap I/O while preserving functional purity), but I
just don't see how that works.

I would love to find a resource that would explain these things for me in a
way I'd understand them.

~~~
tikhonj
The reason that the Monad structure is interesting, in my view, is that it's
simple and neatly captures the notion of a "computation" that we can compose
in different ways.

The core useful operator for monads in Haskell is >>=. It has the following
type:

    
    
        m a -> (a -> m b) -> m b
    

If you squint, this is like function application with a few extra m's thrown
in. Here's normal function application for comparison:

    
    
        a -> (a -> b) -> b
    

So what is this extra m useful for? It's like a hole where we get to plug in
some custom logic. In a sense, it lets us change what it means to "apply" a
"function". This turns out to be useful for a whole bunch of things, not just
IO. (Honestly, from a pedagogical standpoint, I think IO is a bit of a
distraction!)

For example, take the Maybe type. It's Haskell's nullable: a Maybe a means you
either have an a or Nothing.

    
    
        data Maybe a = Just a | Nothing
    

Remembering the signature of >>= above, what's the most natural way to
implement "application"? If we break the function into cases, it becomes
pretty straightforward. Here's the specialized function signature we want to
implement:

    
    
        (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
        x >>= f = ...
    

If x is Nothing then we don't have anything to pass into f so the whole result
has to be Nothing. If x has a value, we can just get that value out and pass
it into f normally, returning the final result.

    
    
        Nothing >>= f = Nothing
        Just x  >>= f = f x
    

(In case you're not familiar with Haskell syntax, the above is actually a
valid definition of (>>=) for Maybe!)

For Maybe, being a monad gives us a standard way of working with values while
automatically dealing with Nothing. It abstracts over repetitive null checking
and lets us easily build up Maybe values based on other Maybe values.

Other examples of monads are the same in spirit. The list monad, for example,
lets us handle _any number of inputs_ in a way that's similar to Maybe. The
State monad similarly lets us combine values while carrying along an implicit
state internally.

The rest of the Monad structure (namely the return function and the laws) are
just a formal way of codifying behavior behavior that's already intuitive.

So how does this all apply to doing input and output?

Well, the problem in Haskell is that it's a language of evaluating expressions
at heart: executing effects makes no sense any more than it would in
arithmetic. To work with effects we instead have a special, opaque type IO;
normal expressions get evaluated to IO actions that can be run to produce the
desired effect—namely the IO type.

Critically, the IO type does not have to be a monad. It could be completely
self-contained and have custom functions for doing one action after the other.
We could imagine something like:

    
    
        after :: IO a -> IO b -> IO b
    

which would let you run an IO statement then run a second one and only return
the value of the last one. It's like an imperative block of code!

However, we would also like some way of _using_ the results of an IO
statement, perhaps assigning them to a name. We can't do this normally because
the IO statements are run separately from expression evaluation. We'd have to
have some sort of function that could take an IO value, unwrap it and do
something with it. And how would we express an interface like this? With a
normal function!

    
    
        doSomething :: IO a -> (a -> IO b) -> IO b
    

Hey, doesn't that look familiar? It's exactly (>>=)!

I'm hand-waving a bit again, but the rest of the monad structure comes up when
you try to make sure after behaves consistently and intuitively.

So IO being a monad emerges naturally from the desire to be able to compose
actions and depend on their results in a way that's _separate_ from normal
variable bindings and expression evaluation.

The causation here is important: it's not that IO _is_ a monad, but rather the
IO type (which _could_ exist on its own) happens to naturally and usefully
_form_ a monad. But it does a lot of other things too, including some specific
capabilities (like spawning threads) that are hard to generalize.

So my point, I suppose, is twofold: monads are useful for combining some
notion of computation and IO happens to be an interesting example, but the
fact that we wrap statements with external effects in a custom type called IO
_does not_ inextricably depend on the idea of a monad.

Did that explanation help? I wrote a blog post on a similar topic that might
be interesting too: [http://jelv.is/blog/Haskell-Monads-and-
Purity](http://jelv.is/blog/Haskell-Monads-and-Purity)

~~~
Guthur
I appreciate your explanation but again it fails to actually show it being
useful outside of the context of working around Haskell's strict type system.
What the OP and myself would like to see is concrete examples why this is a
better approach than the way something would be done without explicitly caring
about monads (I say explicitly because I know there is a tendency to say that
some given structure is a monad and we didn't realise it; but that still
doesn't really prove that it is all that useful in the general sense)

One problem I suppose is that not everyone speaks the abstract language of
monads and so it does not serve much usefulness, yet, and maybe there is a
little chicken and the egg with that.

~~~
AnimalMuppet
I'll take a shot at it.

Quoting tikhoni:

> For Maybe, being a monad gives us a standard way of working with values
> while automatically dealing with Nothing. It abstracts over repetitive null
> checking and lets us easily build up Maybe values based on other Maybe
> values.

To expand on that a bit, in a way that might work for you (or might not): I've
got a function f a that exists. It works. It does _exactly_ what I want.
Unfortunately, the data I've got is a Maybe a, not just an a. So I can't use
my nifty function f, because it takes an a. You can feel my
annoyance/frustration - so near, and yet so far.

But, in fact, I can use my function f, because Maybe is a monad. That lets me
apply f to my Maybe a, _without having to change either f or a whatsoever._ I
don't even have to write the magic code to do this - Maybe already has it.

~~~
rane
Isn't Maybe being an instance of Applicative enough in this case?

~~~
AnimalMuppet
Maybe...

But seriously, I guess I think it's not a Monad. The Monad signature (in this
case) is

    
    
      (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
    

and I think what we really want (and what I said we wanted to do) is

    
    
      Maybe a -> (a -> b) -> Maybe b
    

I don't know whether that's Applicative or Functor, but I don't think it's
Monad.

~~~
Muphrid
That operation is called fmap, and it's characteristic of a functor. In
Haskell, it's called liftM for monads, but they're really the same thing (and
I think recent proposals are going to iron out this redundancy). fmap is one
of the properties of a functor. All monads are functors, so all monads provide
an operation with that signature.

------
IshKebab
Off-topic, but why are Haskell's function signatures written like this?

    
    
        function_name :: Param1Type -> Param2Type -> Param3Type -> ReturnType
    

To me that just makes no sense. There's no way to logically "read" it. The
natural way would be "function_name converts a Param1Type into a Param2Type
and then ... what?! Is this a chain of functions?"

Why not have something sensible like this?

    
    
        function_name :: Param1Type, Param2Type, Param3Type -> ReturnType

~~~
chewxy
Because you write functions like this in mathematics as well.

What we usually see is something like this:

    
    
        f(x) = y
    

But if you look at it carefully the function f is a mapping of the domain x to
the range y (the arrow is f), written like so:

    
    
        x -> y
    

Of course, the domain, being a set of all permissible values, is the type. So
we write the type instead:

    
    
        type1 -> type2
    

Everything in Haskell is a function, and pure functions only ever take ONE
parameter. Therefore the commas make no sense. The name in front just aids in
naming stuff.

If you have a function that takes 2 parameters, they'd have to be curried. How
would you represent curried functions?

    
    
        f::type1 -> type2 -> type3 
    

To make it more concrete, let's look at add instead of f.

So we can define a function like this (let's use Python for its readability):

    
    
        def add(a, b): return a + b
    

But remember, in haskell, functions are pure. Meaning they only map one input
to one output. In order to make this happen, we need to split the function up
into parts that only take one parameter.

Let's start with the plus operator as a function (it is one in Haskell just
made into an infix). To think about it, it'd be something like this:

    
    
        plus(a)(b)
    

Where plus(a) is defined as:

    
    
        def plus(a): return plusA
    

Hence the first part would become a curried function like so, which takes
another parameter:

    
    
        plusA(b)
    

Where plusA() is defined as such:

    
    
        def plusA(b): return b + a # a is a constant
    
    

So if you look at it from the types it was being transformed from:

    
    
        plus() # takes a Real, returns plusA.
    
        plusA(___) # takes a Real, returns a Real. written as (Real -> Real)
    

So if you put it together, the function signature for plus() is:

plus() takes Real -

    
    
        plus :: Real->
    

plus() returns plusA (which is Real->Real) -

    
    
        plus :: Real-> (Real-> Real)
    
    

Or in other words we can write it as such

    
    
        add :: Real -> Real -> Real

~~~
zaptheimpaler
I don't understand one thing about this notation:

    
    
       fun :: A -> B -> C -> D
    

can be bracketed in a bunch of different ways -

    
    
       fun1 :: (A -> B) -> (C -> D)
       fun2 :: A -> (B -> C -> D)

etc. and don't these all mean different things?

fun1 would take a function and return a function, whereas fun2 takes an A and
returns a curried function of B,C -> D.

~~~
Muphrid
The order of operations here is "right associative". So

    
    
      A -> B -> C -> D
    

is equivalent to any of the following

    
    
      A -> (B -> C -> D)
      A -> (B -> (C -> D))
      A -> B -> (C -> D)
    

They're equivalent through currying. But you can only add or remove
parentheses for stuff that is on the right of a function arrow. (A -> B) -> (C
-> D) is a different thing.

------
semigroupoid
I recommend Wadler's very readable paper "Monads for functional programming"
([http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/ba...](http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf))
which defines exactly what Monads in the context of functional programming are
and how they help with IO, State etc.

