3

I am learning from the "Free Applicative Functors". Surely, the question I am going to ask is kind of aside with respect to main idea of the paper, but still...

...on the page 6 there is an attempt to generalize Functor to MultiFunctor:

class Functor f ⇒ MultiFunctor f where
    fmap0 :: a → f a
    fmap1 :: (a → b) → f a → f b
    fmap1 = fmap
    fmap2 :: (a → b → c) → f a → f b → f c
    ...

I can not see how this definition is justified from the category theory's viewpoint: fmap2 seems to be just a bifunctor, i.e. a functor defined on a product category. By definition, product category is given by all possible ordered pairs of objects and morphisms are pairs as well, hence: fmap2 :: (a -> a', b -> b') -> (f a, f b) -> (f a', f b') looks and feels like more appropriate signature.

I can understand the way of thinking standing behing the (a -> b -> c) -> f a -> f b -> f c choice: it is just the most obvious way to take known (a -> b) -> f a -> f b signature and force it to work with binary functions, rather then unary. But isMultiFunctor (given by the definition above) actually a bi-/multifunctor in the sense that category theory expects it to be?

P.S. The reason why I am curious is that it seems like one can't get to the Applicative by generalizing Functor, though paper states that one can.

Zazaeil
  • 3,900
  • 2
  • 14
  • 31
  • 1
    `fmap2 :: (a -> a', b -> b') -> (f a, f b) -> (f a', f b')` can be a nice operation, but this is directly derivable from the standard functor class as `fmap2 (f,g) (x,y) = (fmap f x, fmap g y)`, so there would be no need to have another typeclass. Instead, the original `fmap2 :: (a → b → c) → f a → f b → f c` can not be derived, so it is a real extension (an additional requirement on `f`). – chi Jan 13 '19 at 09:47
  • @chi, that's the point of my question: is `(a -> b -> c) -> f a -> f b -> f c` really a generalization of `Functor` to be a theoretic `Bifunctor`, or just a useful extension from programming point of view, which has nothing to do with true bifunctor? Just wanted to confirm. – Zazaeil Jan 13 '19 at 09:57
  • 1
    I'd say it has nothing to do with bifunctors. That extension, I guess, can be formalized nicely in category theory, and not only in programming, but it's not a bifunctor. To me, "applicative" is an additional requirement on a (unary, covariant) functor between cartesian closed categories, where there's a morphism from `F(b^a)` to `(F b)^(F a)` satisfying a bunch of laws. – chi Jan 13 '19 at 10:52
  • @chi, exactly, see the answer beneath. – Zazaeil Jan 13 '19 at 11:18

2 Answers2

7

I think the category theory angle you are taking is wrong. There is a Bifunctor class (with a map of type (a -> b) -> (c -> d) -> f a c -> f b d) but that is not what this generalisation is. If one uncurries some functions then the signature of fmap2 looks like:

fmap2 :: ((a,b) -> c) -> (f a, f b) -> f c

And by considering fmap2 id, we see that what we are implementing is not a bifunctor but a cartesian functor (i.e. a monoidal functor between cartesian categories), with fmap2 id :: (f a, f b) -> f (a,b) being the natural transformation:

\mu_{x,y} : F(x) \otimes F(y) \to F(x \otimes y)

One can then get an applicative from this Multifunctor generalisation. Just change pure for fmap0 and (<*>) for fmap2 ($).

luqui
  • 59,485
  • 12
  • 145
  • 204
Dan Robertson
  • 4,315
  • 12
  • 17
  • Sure, if you take a moment to go throught page 6, paper states that `fmap2` is generalization of `Functor` to `Multifunctor`. That's why I anchored to the bifunctor. Thank's for reply! – Zazaeil Jan 13 '19 at 10:16
  • Note however that the mere addition of `fmap2` is not sufficient to make a (Haskell) applicative as one also needs a way to construct elements of type `f a`, e.g. `fmap0` or something of type `f ()` that behaves in a sensible way. – Dan Robertson Jan 13 '19 at 10:29
2

Let's start with the obvious: fmap0 is pure.

Here's one you made a mistake on: fmap2 is liftA2.

(bimap is very different - (a -> b) -> (c -> d) -> f a b -> f c d)

And if you go back to the definition of Applicative, you see that it has a default implementation of (<*>), which is liftA2 id, which allows you to define it in terms of pure and either liftA2 or (<*>).

So yes, that class is equivalent to Applicative.

Carl
  • 26,500
  • 4
  • 65
  • 86
  • `(a -> b, c -> d) -> f (a, b) -> f (c, d)` is not very different from what I've mentioned. It is actually the same, as long as you keep in mind that `f a b` is just a pair of objects `f (a, b)` and `(a -> b, c -> d)` is an **uncurried** version, which is isomorphic to the curried one, given by your example. – Zazaeil Jan 13 '19 at 10:01