DEV Community

Brian Berns
Brian Berns

Posted on • Edited on

Contravariants

Normal functors in Haskell are pretty easy to understand:

class Functor f where
  fmap :: (a -> b) -> f a -> f b
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)