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)