Hacker News new | ask | show | jobs
by louthy 2197 days ago
> I'm not sure I follow

Imagine you have a context (the monad), which contains a value of type `a`...

> "by hiding the extraction of the value contained in the monad...

Means, you don't need to care about how we get the value out of the context, it will just be done for you. That's part of the monad's job. Each different type of monad may do it differently (a List will do it many times, an Option will do it zero or one times, Async promises to give you a value, etc. ).

> "... and passing that value to a user supplied lambda that takes a value of the type of the value contained in the monad ..."

So, we have a function called `bind`, which is part of the interface for monads ... it takes the original context (monad) as well as a lambda as arguments.

Below is the signature: `m a` which is the original monad value, (a → m b) is the lambda, and `m b` is the return value, which is a monad with its contextual value type changed from `a` to `b`

    bind :: m a → (a → m b) → m b
Where you see `m a` and `m b`, they're generic types which are parameterisable on both the inner and outer type, the inner type `a` is fairly usual, but the outer-type `m` is a higher-kind and can also be parameterised, `m a` could become `List a` (which is like List<A> in C# or Java).

So, if you squint, you might be able to see that the `a` comes from within the `m a` context, gets passed to the lambda, which needs an `a` argument and returns an `m b`, then that `m b` is returned as the final result.

Each type of monad has its own implementation of `bind`. So, for a List the bind operation looks like this:

    bind :: List a → (a → List b) → List b
And so you should see that for Lists each `a` will be extracted from the first argument (so we'll have zero or many `a` values), each `a` is passed to the lambda which will return a `List b` (and so we have zero or many `List b` values), and those lists of `b` will be concatenated to give a flattened result of `List b`. And so, that's the behaviour if the List monad. Each monad will have its own rules, but must follow the signature of `bind`.

> "...and returning a new monad containing a different type..."

This just means the `a` turns to a `b` (but the `m` stays the same). Essentially it means you can do some kind of mapping of the contextual value(s) to return a new type. So, if you were to map Ints to Strings the bind signature would look like so:

    bind :: List Int → (Int → List String) → List String
In practice you can see how the lambdas returning `m b` allow for chaining of computations. The code below visits the items in two lists (listA and listB) and sums them. The lambdas close over each other to create a context for the `return` at the end of the process. Which is very close to how scoping works in imperative languages.

    bind listA (\a → 
        bind listB (\b → 
            return (a + b)))
The key to any monad is its bind function, that's where its behaviour is. But fundamentally, it's a contract, or interface which forces ordering of computation... the values returned by the lambda (which are monads themselves), clearly can't be evaluated until you evaluate the first argument to get the `a`.

And so, `m a` must run before `m b` ... combine that with `do` notation and you'll get something that looks and feels a lot like imperative code, but all of the side-effects are encapsulated and declared, and all of the logic is pure (referentially transparent).

The example above with `do` notation is:

    a ← listA
    b ← listB
    return (a + b)
Which is essentially equivalent to a nested for loop without the ceremony.
1 comments

This is a good explanation of how the Monad typeclass maps onto data functors, but I'm not sure that it translates well onto Control functors such as (-> r).
I was mostly just trying to clarify and unpack the GP's quite terse statement. Definitely not trying to write a monad tutorial ;)
Oh, sure! In that case, mission accomplished with flying colours :)