Essentially, all the monoidal stuff involved in the methods of a monoidal functor happens on the target category. It can be formalised thus†:
class (Category s, Category t) => Functor s t f where
map :: s a b -> t (f a) (f b)
class Functor s t f => Monoidal s t f where
pureUnit :: t () (f ())
fzip :: t (f a,f b) (f (a,b))
s
-morphisms only come in if you consider the laws of a monoidal functor, which roughly say that the monoidal structure of s
should be mapped into this monoidal structure of t
by the functor.
Perhaps more insightful is to factor an fmap
into the class methods, so it's clear what the “func-”-part of the functor does:
class Functor s t f => Monoidal s t f where
...
puref :: s () y -> t () (f y)
puref f = map f . pureUnit
fzipWith :: s (a,b) c -> t (f a,f b) (f c)
fzipWith f = map f . fzip
From Monoidal
, we can get back our good old Hask-Applicative
thus:
pure :: Monoidal (->) (->) f => a -> f a
pure a = puref (const a) ()
(<*>) :: Monoidal (->) (->) f => f (a->b) -> f a -> f b
fs <*> xs = fzipWith (uncurry ($)) (fs, xs)
or
liftA2 :: Monoidal (->) (->) f => (a->b->c) -> f a -> f b -> f c
liftA2 f xs ys = fzipWith (uncurry f) (xs,ys)
Perhaps more interesting in this context is the other direction, because that shows us up the connection to monads in the generalised case:
instance Applicative f => Monoidal (->) (->) f where
pureUnit = pure
fzip = \(xs,ys) -> liftA2 (,) xs ys
= \(xs,ys) -> join $ map (\x -> map (x,) ys) xs
That lambdas and tuple sections aren't available in a general category, however they can be translated to cartesian closed categories.
†I'm using (,)
as the product in both monoidal categories, with identity element ()
. More generally you might write data I_s
and data I_t
and type family (⊗) x y
and type family (∙) x y
for the products and their respective identity elements.