The Contravariant type class is for functors that define a contramap function with the following type:

def contramap[A, B](fa: F[A])(f: B => A): F[B]

It looks like regular (also called Covariant) Functor’s map, but with the f transformation reversed.

Generally speaking, if you have some context F[A] for type A, and you can get an A value out of a B value — Contravariant allows you to get the F[B] context for B.

Examples of Contravariant instances are Show and scala.math.Ordering (along with cats.kernel.Order).

Contravariant instance for Show.

Say we have a class Money with a Show instance, and a Salary class:

import cats._

import cats.implicits._

case class Money(amount: Int)
case class Salary(size: Money)

implicit val showMoney: Show[Money] = => s"$$${m.amount}")

If we want to show a Salary instance, we can just convert it to a Money instance and show that instead.

Let’s use Show’s Contravariant:

implicit val showSalary: Show[Salary] = showMoney.contramap(_.size)
// showSalary: cats.Show[Salary] = cats.Show$$anon$2@3a2188e0

// res0: String = $1000

Contravariant instance for scala.math.Ordering.

The Show example is trivial and quite far-fetched, let’s see how Contravariant can help with orderings.

The scala.math.Ordering type class defines comparison operations, e.g. compare:, 1)
// res1: Int = 1, 2)
// res2: Int = -1

There’s also a method, called by, that creates new Orderings out of existing ones:

def by[T, S](f: T => S)(implicit ord: Ordering[S]): Ordering[T]

In fact, it is just contramap, defined in a slightly different way! We supply T => S to receive F[S] => F[T] back.

So let’s use it to our advantage and get Ordering[Money] for free:

// we need this for `<` to work
import scala.math.Ordered._
// import scala.math.Ordered._

implicit val moneyOrdering: Ordering[Money] =
// moneyOrdering: Ordering[Money] = scala.math.Ordering$$anon$7@6868063c

Money(100) < Money(200)
// res4: Boolean = true


Contravariant functors have a natural relationship with subtyping, dual to that of covariant functors:

class A
// defined class A

class B extends A
// defined class B

val b: B = new B
// b: B = B@2f3994b1

val a: A = b
// a: A = B@2f3994b1

val showA: Show[A] = => "a!")
// showA: cats.Show[A] = cats.Show$$anon$2@4a306cae

val showB1: Show[B] = showA.contramap(b => b: A)
// showB1: cats.Show[B] = cats.Show$$anon$2@4b646984

val showB2: Show[B] = showA.contramap(identity[A])
// showB2: cats.Show[B] = cats.Show$$anon$2@118f7b3d

val showB3: Show[B] = Contravariant[Show].narrow[A, B](showA)
// showB3: cats.Show[B] = cats.Show$$anon$2@4a306cae

Subtyping relationships are “lifted backwards” by contravariant functors, such that if F is a lawful contravariant functor and B <: A then F[A] <: F[B], which is expressed by Contravariant.narrow.