I'm going mental with Chapter 8 from Category Theory for Programmers.
In section 8.3, Bartosz defines this type
newtype BiComp bf fu gu a b = BiComp (bf (fu a) (gu b))
Here, if I'm understanding a bit of Haskell, bf, fu, and gu are type constructors, bf of kind (* -> *) -> (* -> *) -> * -> * -> *, and fu and gu of kind * -> * (just like Maybe or []), whereas a, and b are general types of kind *; BiComp on the left of = is a type constructor of kind to long to write, whereas BiComp on the right is a value constructor, so it's of type (bf (fu a) (gu b)) -> BiComp bf fu gu a b.
Then the author makes BiComp a bifunctor in a and b, provided that the type constructor parameter bf is a Bifunctor too, and that the type constructors fu and gu are Functors:
instance (Bifunctor bf, Functor fu, Functor gu) => Bifunctor (BiComp bf fu gu) where
bimap f1 f2 (BiComp x) = BiComp ((bimap (fmap f1) (fmap f2)) x)
So far so good, all seems reasonable to me at this point. Except that using the same name for the type constructor and value constructor is probably getting me lost.
Now I'm tempted to make the following observations:
- the
bimapon the right of the definition is the one leveraging the constraints: it's thebimapthat is assumed as defined in theBifunctorinstance of whatever type contructorbfreally is, therefore thisbimaphas type(a -> a') -> (b -> b') -> bf a b -> bf a' b'; I think this is less interesting than the following, because it's after all just the signature ofbimapfor theBifunctortypeclasspresented in 8.1; - the
bimapon the left, instead, is the one we are defining to makeBiCompaBifunctorin its 4th and 5th parameters; and the argumentsf1andf2are function that have to act on entities of type which are those 4th and 5th parameters ofBiComp; therefore, thisbimap's type is(a -> a') -> (b -> b') -> BiComp bf fu gu a b -> BiComp bf fu gu a' b'.
Is this correct?
If so, then I don't understand the following
bimap :: (fu a -> fu a') -> (gu b -> gu b') -> bf (fu a) (gu b) -> bf (fu a') (gu b')
because this is the type of the bimap on the right, the one I wrote in the bullet point above, except that it's written with a = fu a, a' = fu a', and so on.
Am I missing something (or overthinking...)?
You're pretty close.
First, you have the kind of
bfwrong. It's actually just* -> * -> *, which is exactly as expected given that it's going to be aBifunctor. Of course, the kind ofBiCompis pretty crazy:As for the types in your bullet points, technically, they're both correct, but it might help to use fresh type variables (especially for the type in your first bullet point!) to make this all a little clearer. In fact, the
bimapon the right hand side has the typeWe need to use this to make something that converts our input value, of type
bf (fu a) (gu b)to an output value of typebf (fu a') (gu b'). We can do this only if we letc ~ fu a, c' ~ fu a', d ~ gu b, d' ~ gu b'. Let's take a look at what that does to our RHSbimap:Aha! That's exactly what you found on the right hand side! And, we can provide exactly the arguments we need. First, a function of type
fu a -> fu a'. Well, we have a given functionf1 :: a -> a'and we know thatfuis a functor, so we can get the function we need withfmap f1. Likewise withf2andfmap f2, and everything works out nicely.