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 Functor
s:
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
bimap
on the right of the definition is the one leveraging the constraints: it's thebimap
that is assumed as defined in theBifunctor
instance of whatever type contructorbf
really is, therefore thisbimap
has 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 ofbimap
for theBifunctor
typeclass
presented in 8.1; - the
bimap
on the left, instead, is the one we are defining to makeBiComp
aBifunctor
in its 4th and 5th parameters; and the argumentsf1
andf2
are 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
bf
wrong. It's actually just* -> * -> *
, which is exactly as expected given that it's going to be aBifunctor
. Of course, the kind ofBiComp
is 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
bimap
on 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 thatfu
is a functor, so we can get the function we need withfmap f1
. Likewise withf2
andfmap f2
, and everything works out nicely.