Initually, functor is a mapping from one category to another, which preserves the structure of the origin one.
Formally, functor is defined as
A functor $F:C→D$ between categories $C$ and $D$ is a mapping of objects to objects and arrows to arrows, in such a way that:
- $F(g\cdot f)=F(g)\cdot F(f)$
class Functor f where fmap ∷ (a → b) → f a → f b
A contravariant functor is like a functor but it reverses the directions of the morphisms. —— nLab
Basically it's the "reversed" version of covariant functor.
class Contravariant f where contramap ∷ (b → a) → f a → f b
Bifunctor is a functor which domain is the product of two categories.
For $C_1$, $C_2$ and $D$ categories, a functor $F:C_1\times C_2→D$ is a bifunctor.
class Bifunctor f where bimap ∷ (a → c) → (b → d) → f a b → f c d
Examples of bifunctor include
instance Bifunctor (,) where bimap f g (x , y) = (f x , g y) instance Bifunctor Either where bimap f g = either (Left . f) (Right . g)
A Profunctor is just a bifunctor that is contravariant in the first argument and covariant in the second. What's the problem? —— School of Haskell
So we just "reverse" the first argument of
class Profunctor f where dimap ∷ (c → a) → (b → d) → f a b → f c d
A good example of profunctor is
instance Profunctor (->) where dimap ca bd ab = bd . ab . ca
If you are familiar with "tradition" programming languages like Java or C#, you may have heard of "covariant" and "contravariant" before. For example, if type
A is subtype of
IEnumerable<A> is subtype of
IEnumerable<T> is covariant on
T. And similarly,
Action<T> is contravariant on
T. We can regard covariant and contravariant functors as more abstract (we weaken relationship from subtype to arbitrary binary relation) version of these types.