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.
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.
unitis simple, as I’ve described in my comment:def unit(a): return aMap 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_lstThen 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
mapandbind)This allows us to define
bindfrom my other comment (which you callflatMap) in terms ofjoinandmap:def bind(lst, fun): return join(map(fun, lst))Or, if you already have a
bind(andunit) defined, you can definejoinandmapas 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
unitandbindis equivalent to a type definingunit,joinandmap- they are both equivalent definitions of a monad, you can derive one from another and vice versa.Historically, the
unit+binddefinition 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.