cats.Functor

Functor is a higher-kinded type of arity one. It accepts a unary type parameter F[_]. In other words, its type parameter must be a type that has a type parameter itself; for instance, Option[A], Vector[A], Future[A], and so on. It declares a map method that can transform the elements inside of F. Here is a simplified definition of cats.Functor:

trait Functor[F[_]] {
  def map[A, B](fa: F[A])(f: A => B): F[B]

This should be familiar. We have already seen several classes of the SDK that define a map function doing the same thing: Vector, Option, and so on. Hence, you might wonder why you would ever need to use an instance of Functor[Option] or Functor[Vector]; they would only define a map function that is already available.

One advantage of having this Functor abstraction in Cats is that it lets us write more generic functions:

import cats.Functor
import cats.implicits._
def addOne[F[_] : Functor](fa: F[Int]): F[Int] = fa.map(_ + 1)

This function adds 1 for any F[Int] that has a Functor type class instance. The only thing I know about F is that it has a map operation. Hence, this function will work for many parameterized types such as Option or Vector:

addOne(Vector(1, 2, 3))
// res0: Vector[Int] = Vector(2, 3, 4)
addOne(Option(1))
// res1: Option[Int] = Some(2)
addOne(1.asRight)
// res2: Either[Nothing,Int] = Right(2)

Our function, addOne, applies the principle of the least power; given a choice of solutions, it picks the least powerful solution capable of solving your problem.

We use a parameter type that is more generic, and hence, less powerful (it only has one map function). This makes our function more reusable, easier to read, and easier to test:

  • More reusable: The same function can be used with Option or Vector or List, or anything that has a Functor type class instance.
  • Easier to read: When you read the signature of addOne, you know that the only thing it can do is transform the elements inside of the F. It cannot, for instance, shuffle the order of the elements, nor can it drop some elements. This is guaranteed by the Functor laws. Therefore, you do not have to read its implementation to make sure that it does not get into any mischief.
  • Easier to test: You can test the function with the simplest type that has a Functor instance, which is cats.Id. the code coverage will be the same. A simple test would be, for instance, addOne[cats.Id](1) == 2.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset