tl;dr Here's a really short explanation for JavaScript, as in just the flatmap part.
map
is pretty well understood in JavaScript (and I'm assuming you understand it).
So you 'just' need to make the leap to flatmap. Which is mapping something and flattening the result.
Flattening a JavaScript array is concatenating a 2D array into an array.
Another attempt at explaining monads, using just Python lists and the map
function. I fully accept this isn't a full explanation, but I hope it gets at the core concepts.
I got the basis of this from a funfunfunction video on Monads and the Learn You A Haskell chapter 'For a Few Monads More'. I highly recommend watching the funfunfunction video.
At it's very simplest, Monads are objects that have a map
and flatMap
functions (bind
in Haskell). There are some extra required properties, but these are the core ones.
flatMap
'flattens' the output of map, for lists this just concatenates the values of the list e.g.
concat([[1], [4], [9]]) = [1, 4, 9]
So in Python we can very basically implement a list Monad with just these two functions:
# helper function as python doesn't have concat
def concat(lst):
return sum(lst, [])
# monad magic
def flatMap(func, lst):
return concat(map(func, lst))
func
is any function that takes a value and returns a list e.g.
lambda x: [x*x]
For clarity I created the concat
method in Python via a simple function, which sums the lists i.e. [] + [1] + [4] + [9] = [1, 4, 9]
(Haskell has a native concat
method).
I'm assuming you know what the map
function is e.g.:
>>> list(map(lambda x: [x*x], [1,2,3]))
[[1], [4], [9]]
Flattening is the key concept of Monads and for each object which is a Monad this flattening allows you to get at the value that is wrapped in the Monad.
Now we can call:
>>> flatMap(lambda x: [x*x], [1,2,3])
[1, 4, 9]
This lambda is taking a value x and putting it into a list. A monad works with any function that goes from a value to a type of the monad, so a list in this case.
That's your list monad defined.
You can now compare this to a python list comprehension:
>>> [x*x for x in [1,2,3]]
[1, 4, 9]
Other examples that aren't lists are JavaScript Promises, which have the then
method and JavaScript Streams which have a flatMap
method.
So Promises and Streams use a slightly different function which flattens out a Stream or a Promise and returns the value from within.
The Haskell list monad has the following definition:
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
i.e. there are three functions return
(not to be confused with return in most other languages), >>=
(the flatMap
) and fail
.
Hopefully you can see the similarity between:
xs >>= f = concat (map f xs)
and:
def flatMap(f, xs):
return concat(map(f, xs))