The monoid that’s referred to with “monoidal functor” is not a Monoid
monoid, i.e. a value-level monoid. It’s a type-level monoid instead. Namely, the boring product monoid
type Mempty = ()
type a <> b = (a,b)
(You may notice that this is not strictly speaking a monoid; it’s only if you consider ((a,b),c)
and (a,(b,c))
as the same type. They are sure enough isomorphic.)
To see what this has to do with Applicative
, resp. monoidal functors, we need to write the class in other terms.
class Functor f => Monoidal f where
pureUnit :: f Mempty
fzip :: f a -> f b -> f (a<>b)
-- an even more “general nonsense”, equivalent formulation is
-- upure :: Mempty -> f Mempty
-- fzipt :: (f a<>f b) -> f (a<>b)
-- i.e. the functor maps a monoid to a monoid (in this case the same monoid).
-- That's really the mathematical idea behind this all.
IOW
class Functor f => Monoidal f where
pureUnit :: f ()
fzip :: f a -> f b -> f (a,b)
It’s a simple exercise to define a generic instance of the standard Applicative
class in terms of Monoidal
, vice versa.
Regarding ("ab",(+1)) <*> ("cd", 5)
: that doesn’t have much to do with Applicative
in general, but only with the writer applicative specifically. The instance is
instance Monoid a => Monoidal ((,) a) where
pureUnit = (mempty, ())
fzip (p,a) (q,b) = (p<>q, (a,b))