λ Tony's Blog λ

Functors and things using Scala

Posted on March 1, 2013

Types of Functors

There are many types of functors. They can be expressed using the Scala programming language.

  • covariant functors — defines the operation commonly known as map or fmap.
// covariant functor
trait Functor[F[_]] {
  def fmap[A, B](f: A => B): F[A] => F[B]
}
  • contravariant functors — defines the operation commonly known as contramap.
// contravariant functor
trait Contravariant[F[_]] {
  def contramap[A, B](f: B => A): F[A] => F[B]
}
  • exponential functors — defines the operation commonly known as xmap. Also known as invariant functors.
// exponential functor
trait Exponential[F[_]] {
  def xmap[A, B](f: (A => B, B => A)): F[A] => F[B]
}
  • applicative functor1 — defines the operation commonly known as apply or <*>.
// applicative functor (abbreviated)
trait Applicative[F[_]] {
  def apply[A, B](f: F[A => B]): F[A] => F[B]
}
  • monad2 — defines the operation commonly known as bind, flatMap or =<<.
// monad (abbreviated)
trait Monad[F[_]] {
  def flatMap[A, B](f: A => F[B]): F[A] => F[B]
}
  • comonad3 — defines the operation commonly known as extend, coflatMap or <<=.
// comonad (abbreviated)
trait Comonad[F[_]] {
  def coflatMap[A, B](f: F[A] => B): F[A] => F[B]
}

Remembering the different types

Sometimes I am asked how to remember all of these and/or determine which is appropriate. There are many answers to this question, but there is a common feature of all of these different types of functor:

They all take an argument that is some arrangement of three type variables and then return a function with the type F[A] => F[B].

I memorise the table that is the type of the different argument arrangements to help me to determine which abstraction might be appropriate. Of course, I use other methods, but this particular technique is elegant and short. Here is that table:

functor argument arrangement
covariant A => B
contravariant B => A
exponential (A => B, B => A)
applicative F[A => B]
monad A => F[B]
comonad F[A] => B

We can see from this table that there is not much reason to emphasise one over the other. For example, monads get lots of attention and associated stigma, but it’s undeserved. It’s rather boring when put in the context of a bigger picture. It’s just a different arrangement of its argument (A => F[B]).

Anyway, this table is a good way to keep a check on the different types of abstraction and how they might apply. There are also ways of deriving some from others, but that’s for another rainy day. That’s all, hope it helps!


  1. applicative functors also define an identity operation (def insert[A]: A => F[A]) however, it is omitted.↩︎

  2. monads also define an identity operation (def insert[A]: A => F[A]) however, it is omitted.↩︎

  3. comonads also define an identity operation (def extract[A]: F[A] => A) however, it is omitted.↩︎