Monad is (a classes of type of) a collapsible container. Collapsible, like how you can flatten a nested list, or Option<Option<A>> can be flattened to Option<A>.
A common pattern with monads is the flatMap function, where you apply function A -> List<B> to (each element of) List<A> to obtain List<B>. This happen to represent erroneous call chaining with Option or Result types.
Showing that a type defining unit and bind is equivalent to a type defining unit, join and map - they are both equivalent definitions of a monad, you can derive one from another and vice versa.
Historically, the unit+bind definition is more popular and aligns with what most functional languages do, so I went with it for my explanation. But yours is probably a bit easier to understand for an FP outsider.
Monad is (a classes of type of) a collapsible container. Collapsible, like how you can flatten a nested list, or Option<Option<A>> can be flattened to Option<A>.
A common pattern with monads is the flatMap function, where you apply function A -> List<B> to (each element of) List<A> to obtain List<B>. This happen to represent erroneous call chaining with Option or Result types.
This is actually a good alternative explanation, as long as you clarify it a bit.
What is meant by “container” type is that it’s possible to
unit
/pure
)map
/fmap
), without “taking any values out”Take for example a list.
unit
is simple, as I’ve described in my comment:def unit(a): return a
Map is also fairly straight-forward, just applying a given function to every element of the list:
def map(fun, lst): new_lst = [] for element in lst: new_lst.append(fun(element)) return new_lst
Then your final operation (“flattening” the container) is traditionally called
join
. It concatenates a list of lists into a single flat list:def join(lst): new_lst = [] for element in lst: new_lst.extend(element) return new_lst
(you might notice that it is extremely similar to both
map
andbind
)This allows us to define
bind
from my other comment (which you callflatMap
) in terms ofjoin
andmap
:def bind(lst, fun): return join(map(fun, lst))
Or, if you already have a
bind
(andunit
) defined, you can definejoin
andmap
as follows:def join(lst): return bind(lst, unit) def map(fun, lst): return bind(lst, lambda x: unit(fun(x)))
Showing that a type defining
unit
andbind
is equivalent to a type definingunit
,join
andmap
- they are both equivalent definitions of a monad, you can derive one from another and vice versa.Historically, the
unit
+bind
definition is more popular and aligns with what most functional languages do, so I went with it for my explanation. But yours is probably a bit easier to understand for an FP outsider.