I'm having some trouble "mentally type checking" the relationship between bisequence and bitraverse in Haskell. Here are their signatures, according to the documentation:
bisequence :: (Bitraversable t, Applicative f) => t (f a) (f b) -> f (t a b)
bitraverse :: Applicative f => (a -> f c) -> (b -> f d) -> t a b -> f (t c d)
Also according to the documentation, the following relationship holds:
bisequence ≡ bitraverse id id
But this relationship doesn't make sense to me:
The signature of
idisa -> a, so how can it be accepted as the first or second argument tobitraverse, which wants e.g.a -> f c?Even if we overcome the first point, the type of
bitraverse id idshould beBitraversable t => t a b -> f (t c d), right? But that's not the same as the type ofbisequence; it is missing an applicative later on theaandbin the input.
What am I missing? I'm sure these two misunderstandings are related, because they are both about missing applicative layers in the type.
Let's break down all of the variables.
Now we have
id :: x -> x(I'm using a different variable name here so we don't get confused using the same name to mean two different things). Then when we writebitraverse id, we have thatidmust unify with the first argument ofbitraverse, so(
~is the unification relation)Hence,
a ~ xandf c ~ x. Substituting that in gives usRinse and repeat for the second
id, which we'll say has type signatureid :: y -> y. We want to unifySo
b ~ yandf d ~ y, by the same logic. Hence,which is
by another name.
Notably, when we said the argument to
bitraversewasa -> f c, we never said thatawasn't equal tof c.acould be literally anything, and in this case we chose it to be equal tof c(mandated by unification with the type ofid).