Also Bind or the >>= operator is the worst way to explain monads. Flatmap is a synonym for Bind which makes everything even more confusing. If your intuition around monads centers around the >>= operator then your intuition is incomplete. This infatuation with BIND as the central concept around monads is what screws everything up, it leaves people with broken intuition about monads and it also makes learning 1000x harder.
Better to explain monads in terms of functions.
A function (F) is something that takes an input A and returns an output B.
F(A) -> B
You can compose functions. Combine 2 functions into one function to produce a bigger function so long as the types match. We often overload an operator for function composition: F(A) -> B
F2(B) -> C
F3 = F2 * F = compose(F2, F)
compose(F, F2) = func(A) -> C {return F2(F(A))}
You can create bigger functions by composing lots of tiny functions F5 = F4 * F3 * F2 * F
A monad is simply a special type of function that returns something in "addition" to the return type. One way to think of it is it's like Golang where the function returns an additional error value, another way to think of it is it returns the function as a list with additional values appended.normal function:
F(A) -> B
monad: M(A) -> (B, error)
Nothing crazy here, M is a monad we can give it a name call it the "golang error monad."Why does this matter? Well For one thing. Monads should be able to compose like functions.
You should be able to:
M5 = M4 * M3 * M2 * M1
How would that work? Imagine two monads: M(A) -> (B, error) = func(A){return (A, nil) if A.correct() else (nil, error.New()) }
M2(B) -> (C, error) = func(B){return B, nil) if B.correct() else (nil, error.New()) }
M2 * M - compose(M2, M)
compose(M2, M) = func(A){
B, err = M(A)
if err {
return nil, err
} else {
return M2(B)
}
}
It's monads can be composed like functions it's just a little different. For the golang error monad as soon as one monad in the composition chain returns an error just make the whole thing an error.In haskell the compose operator for monads is F1 >=> F2 for functions its F1 . F2
So that's all it is. Functions should have the property to compose. Monads should be like functions with the results wrapped in a burrito, but they also should be able to "compose" as well. It's like a higher level version of a function.
That's all.
Typically in languages you create functions by defining functions. Normal. In languages with first class support for monads, you define the monads like functions, BUT after that you HAVE to define how the monads compose. That completes the definition of a monad and function.
Functions need to be defined in terms of how they compose as well, but you typically don't do this in other languages as those languages just have you do it explicitly F(A) = F1(F2(A)) or they have the compose operator predefined.