Monads in category theory is defined by triples T, unit, flat⟩.
class Monad t where
map :: (a -> b) -> (t a -> t b) -- functorial action
unit :: a -> t a
flat :: t (t a) -> t a
class KleisliTriple t where
unit :: a -> t a
flatMap :: t a -> (a -> t b) -> t b
KleisliTriple flats the structure by the operator: flatMap
(or bind
in Haskell) that is composition of map
and flat
.
However, I always think it's much simpler and easier to understand and implement the Monad conept in functional programming to compose functions by flatten the structure with the object such as flatUnit
that is composition of unit
and flat
.
In this case, flatUnit(flatUnit(x)) = flatUnit(x)
. I actually implemented in this manner in JavaScript, and with flatUnit
and map
(just a legacy functor operator), all the benefit of Monad seems to be obtained.
So, here's my question.
I have kept looking for documents about the kind of flatUnit
formalization in functional programming, but never found it. I understand there's a historical context that Eugenio Moggi who first discovered the relevance of monads in functional programming, and in his paper that happened to be KleisliTriple application, but since Monads are not limited to Kleisli Category and considering the simplicity of flatUnit
, to me it's very strange.
Why is that? and what do I miss?
EDIT:code is removed.
In this answer, I won't dwell on
flatUnit
. As others have pointed out,join . return = id
for any monad (it is one of the monad laws), and so there isn't much to talk about it in and of itself. Instead, I will discuss some of the surrounding themes raised in the discussion here.Quoting a comment:
This, I believe, is the heart of the question. A monad need not be a functor with a flat structure, but a functor whose values can be flattened (with
join
) in a way that follows certain laws ("a monoid in the category of endofunctors", as the saying goes). It isn't required for the flattening to be a lossless operation (i.e. forjoin
to be an isomorphism).Monads whose
join
is an isomorphism are called, in category theory parlance, idempotent monads 1. For a HaskellMonad
to be idempotent, though, the monadic values must have no extra structure. That means most monads of immediate interest to a programmer won't be idempotent (in fact, I'm having trouble to think of idempotent HaskellMonad
s that aren'tIdentity
or identity-like). One example already raised in the comments was that of lists:The function/reader monad gives what I'd say is an even more dramatic illustration:
This recent question gives an interesting illustration involving
Maybe
. The OP there had a function with signature...... and used it like this...
... thus obtaining a
Maybe (Maybe (Integer, Integer))
result. The two layers ofMaybe
correspond to two different ways of failing: ifu
,v
orw
areNothing
, we getNothing
; if the three of them areJust
-values butappFunc
results inNothing
, we getJust Nothing
; finally, if everything succeeds we get aJust
-value within aJust
. Now, it might be the case that we, like the author of that question, didn't care about which layer ofMaybe
led to the failure; in that case, we would discard that information, either by usingjoin
on the result or by rewriting it asu >>= \x -> v >>= \y -> w >>= \b -> appFunc x y b
. In any case, the information is there for us to use or discard.Note 1: In Combining Monads by King and Wadler (one of Wadler's papers about monads), the authors introduce a different, and largely unrelated, meaning for "idempotent monad". In their sense, an idempotent monad is one for which (in applicative notation)
f <$> u <*> u = (\x -> f x x) <$> u
-- one example would beMaybe
.