Invariant Functors Unlocked
We tend to forget this quite often. An invariant functor or an exponential functor is, given A => B
and B => A
, it converts type A
to typeB
in the same context F[_].
We call this xmap
.
Covariant Functor
That’s the famous Functor
! Covariant functor implements xmap
by discarding the function B => A
.
Contravariant Functor
As expected, it discards f: A => B
and makes use of contramap
to implement xmap
Example for Contravariant Functor
Usage
Example for covariant Functor
As expected, it is DecodeJson
, where the type parameter in the type class comes at covariant position (method result)
Note
If type parameters are at covariant position, that means the method return contains the type.
If type parameters are at contravariant position, that means the method parameters contain the type.
When is invariant functor?
We may have types at covariant (output) or contravariant (input) position. However, we may sometime deal with both covariance and contravariance in the same type class.
Let’s bring in EncodeJson and DecodeJson into one type class.
EncodeJson and DecodeJson
Functor but invariant
So an individual map
or contramap
to upcast (or downcast) an A to B in the context of F[_]
is not possible if F
has types both in covariant and contravariant positions. It means, F
has to have an invariant functor for it!
Apply, Applicative to Divide, Divisible
We saw the contra-variant version of functor. We will see the contra-variant version of Apply
, which is called Divide
and how that can be more powerful if EncodeJson
had an instance of Divide
in the next blog. Along with it, we will discuss more on Apply
and Applicative
and how Divide
and Divisible
form the hierarchy!
Originally published at gist.github.com.