Monoidal functor
I was wondering what it meant when it comes to Xyz functor such as monoidal functor. It turned out that it meant a functor with additional properties in addition to the functor properties. For example, monoidal functor is a functor that preserves a monoidal structure.
Let’s take an example of product of types. When you have a
and b
, you can have their product type (a, b)
, but this forms a monoidal structure with ()
as an identity. It satisfies ((a, b), c) ≡ (a, (b, c))
, (a, ()) ≡ a
, and ((), a) ≡ a
.
When a functor preserves this monoidal structure, you can apply fmap
then create a product, or create a product then apply fmap
to get the same result.
For example, imagine you have 10
and "foo"
and apply Maybe
as the functor. You’ll get (Just 10, Just "foo")
when you first apply fmap
then create a product, and get Just (10, "foo")
when you first create a product and apply fmap
after that.
As you can see, you can define such function like this.
f :: (Maybe a, Maybe b) -> Maybe (a, b)
f (Just x, Just y) = Just (x, y)
f _ = Nothing
You also need a function to convert a unit to a unit to make it preserve the associativity.
e :: () -> Maybe ()
e () = Just ()
Let’s make it more generic using type classes.
{-# LANGUAGE FlexibleInstances,
InstanceSigs,
ScopedTypeVariables,
UndecidableInstances
#-}
class Functor f => Monoidal f where
mu :: (f a, f b) -> f (a, b)
epsilon :: () -> f ()
This Monoidal
class represents a functor that preserves a monoidal structure in terms of product. You can use the implementation of f
and e
above to make Maybe
an instance of Monoidal
.
instance Monoidal Maybe where
mu :: (Maybe a, Maybe b) -> Maybe (a, b)
mu (Just a, Just b) = Just (a, b)
mu _ = Nothing
epsilon :: () -> Maybe ()
epsilon () = Just ()
Interestingly enough, Monoidal
is identical to Applicative
, so you can define Applicative
in terms of Monoidal
.
instance (Functor f, Monoidal f) => Applicative f where
(<*>) :: forall a b. f (a -> b) -> f a -> f b
ff <*> fa = let fp :: f (a -> b, a) = mu (ff, fa)
in fmap (\(f, a) -> f a) fp
pure :: a -> f a
pure a = fmap (const a) (epsilon ())