Normal functors in Haskell are pretty easy to understand:
class Functor f where
fmap :: (a -> b) -> f a -> f b
This just says that if you know how to convert an a into a b, then you can also convert a functor of a into a functor of b. For example, String.length is a String -> Int function, so it can be used to convert a list of strings into a list of integers: [String] -> [Int]. This sort of garden-variety functor is called "covariant".
Contravariant functors look similar, but are used to preprocess input before it reaches a function:
-- like a normal functor, but "f b" and "f a" are reversed
class Contravariant f where
contramap :: (a -> b) -> f b -> f a
f b and f a are contravariants, but you can think of them as functions that take a b and an a as input, respectively:
-- contravariant instance for raw functions
contramap :: (a -> b) -> (b -> x) -> (a -> x)
This says that if you know how to convert an a into a b, and you have a function that takes a b as input, then you can create a function that takes an a as input instead. Easy peasy.
Now that you understand covariance and contravariance, you can combine them to get a profunctor.
Top comments (0)