I've read this Q&A but don't understand the category theory part.
Here is my reasoning so far: When I look at the types
F (a -> b) -> F a -> F b
(a -> M b) -> M a -> M b
a -> F a
a -> M a
the only portion that resembles a monoid at the type level is the type constructor, i.e. the applicative/monadic context:
// binary operation
F -> F -> F
M -> M -> M
// identity element
F
M
So I'd say applicatives/monads are monoidal in terms of their contexts, because they combine two contexts of the same type into one. pure
/return
creates a most minimal context, so we can think of it as the identity context similar to the identity element of a monoid, which creates a "most minimal value".
However, monads/applicatives are not monoidal in their type parameter, because they include a transformation from a
to b
.
I am not sure my reasoning makes any sense. What baffles me is that monoids on the one hand and applicatives/monads on the other combine things differently:
Nothing <> (Just "bar") -- Just "bar"
(++) <$> Nothing <*> (Just "bar") -- Nothing
Nothing >>= (\x -> (Just "bar") >>= (return . (++) x)) -- Nothing
However, I guess the different result values are due to monoids interpret expressions as ordinary values whereas applicatives/monads interpret expressions as computations (one that may fail in the above example).
Now in the aforementioned Q&A is stated monads are monoidal in the category of endofunctors and applicatives are lax monoidal functors. I don't fully understand that but clearly applicatives only partially preserve the monoidal structure whereas monads fully preserve it. What are the practical implications of this difference from a perspective of a functional programmer?
I ask this question as part of the attempt to better understand applicative/monads and what causes there different expressiveness.
Monoid
forMaybe
works that way has nothing to do with the theory. We could have easily defined(<>) @ Maybe
to beliftA2 (<>)
; the fact that it isn't is more of a historical accident than anything else. – Lambda Fairy(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
, or its flipped version(>=>)
, rather than the application operator(>>=)
. The monad laws are then identical to the monoid laws, namely left identitypure >=> x
=x
, right identityx >=> pure
=x
, and associativity(x >=> y) >=> z
=x >=> (y >=> z)
. – Jon Purdy