I will be necessarily somewhat long, somewhat wrong and mostly fuzzy, given my own level of understanding, but I am not one to shy away from making a fool of oneself, in the quest for knowledge.
Monads (and related concepts from category theory, like Functor, Applicative etc) are the next level of abstraction in Functional Programming, beyond functions.
FunctionsFunctions f : A => B allow you to think and program a certain way. Once you get used to this functional programming, you can move on to higher types, higher functions and from there to monads.
A function is basically a transformation: it turns an A into a B. In functional programming, one can take it and do stuff with it.
FunctorsFunctors F[A] lift a simple function to something more - the signature tells you their secret:
map[B] (f : A => B) : F[A] => F[B]
... they essentially gobble up your function f and return something higher level - pause and reflect on this for a second.
This map or fmap, you could think of it as taking a simple unix command like "wc -l" and apply it to something, as in "find *.txt | wc -l"
In scala, you are used to a slightly different version of the same idea:
List(1,2,3) map (_ + 4)
Why are they useful? Many reasons
For instance – you are normally ‘forbidden’ from using state between two calls to f – you should know that by now. However, inside a functor you could, for the duration of the entire transformation F[A] => F[B]… you could have some state there… this is the beauty of an internalized iterator versus the one you’re used to. That state would be well encapsulated there in that transformation, so it could be used.
How that transformation is executed, is up to the specific functor you use. Some can optimize it, some can be dumb while some can use state (like cache a DB connection between calls or whatever).
Let’s have a quick random example: I could have my own functor, working on lists, which keeps the elements sorted. If I apply a random function to it, the result has to be also sorted. You can see the problem? my functor will keep it sorted while an externalized loop may or may not keep it sorted, depending on the programmer.
MySortedList (1,2,3) map (rand(_))will always be sorted, while List (1,2,3) map (rand(_)) is not…
Without functions and functors, there is no way you could express and enforce that idea – I don’t think…
MonadsMonads go even further. Monads have certain laws which give them certain properties which are very useful once you get used to thinking in those terms.
Monads use flatMap rather than map, with a signature
flatMap (f : A => M[B]) : M[A] => M[B]
As you can see, they also return a transformation, but a yet higher level one, which includes flattening. But because it includes flattening, it can do it in whichever way it wants.
Why monads are more useful than functors – look at the gobble-able: it’s an f : A => M[B] rather than f : A => B – the functors limit you to the shape of the functor, sort of speak – basically if you start with a list of ID’s you will end up with a list of Johns of the same size (or more or less, if the functor is cheating).
Monads are one better, you can start with a list of 5 student ID’s and end up with either 45 grades in a school year or 2 missing registrations… yes, f : A => B has to return exactly one and the same B for an A, while an f : A => M[B] could return for instance empty (called unit) he he…
Keep readingThere’s a lot more to it, as others are trying to convey – try to read as much as you can – there’s no one angle that makes it easy to jump to monad abstractions…
If you are confused by the signatures I used above, it’s ok – no, you don’t have to learn Haskell – there’s an entire series of blog posts to explain the gap...
Note that the random sample above is not kosher, since rand() is not a pure function: it never returns the same B for a given A... but it makes a good point.