Functors

A functor is one of those terms that comes from category theory in mathematics and causes a lot of pain to developers who come into functional programming and have less mathematical background. It is a requirement for monads, and here we will try to explain it in a way that would be easy to understand.

What is a functor? In the preceding section, we looked at monoids as a way to abstract some computation and then used them in different ways for optimization or to create more complex computations. Even though some people might not agree with the correctness of this approach, let's look at functors from the same point of view—something that will abstract some specific computations.

Note

In Scala, a functor is a class that has a map method and conforms to a few laws. Let's call them functor laws.

The map method for a functor of the F[T] type takes a function from T to Y as a parameter and returns a F[Y] as a result. This will become much clearer in the next subsection, where we will show some actual code.

Functors also obey some functor laws:

  • Identity: Whenever the identity function is mapped over some data, it doesn't change it. In other words, map(x)(i => i) == x.
  • Composition: Multiple maps must compose together. It should make no difference if we do this operation: x.map(i => y(i)).map(i => z(i)) or x.map(i => z(y(i))).
  • The map function preserves the structure of the data, for example, it does not add or remove elements, change their order, and so on. It just changes the representation.

The preceding laws give developers some grounds to assume certain things when performing different computations. For example, we can now safely postpone different mappings of data in time or just do them all together, and be sure that the final result will be the same.

From what we mentioned previously, we can actually come up with a conclusion that functors set a specific set of laws on their operations (map in this case) that must be in place and allow us to automatically reason about their results and effects.

Now that we have a definition for functors and we showed the laws they should follow, in the next subsection we can create a base trait that all functors will be able to extend.

Functors in real life

Before we show an example functor trait based on the laws we showed in the preceding section, you can conclude that standard Scala types such as List, Option, and others that define a map method are functors.

If we want to create our own types that follow the functor laws, we can create a base trait and make sure to implement it:

trait Functor[F[_]] {
  def map[T, Y](l: F[T])(f: T => Y): F[Y]
}

Let's now create a list functor that will simply call the map function of the Scala List:

package object functors {

  val listFunctor = new Functor[List] {
    override def map[T, Y](l: List[T])(f: (T) => Y): List[Y] = l.map(f)
  }
}

In the preceding code, the fact that an object is a functor simply allows us to assume that certain laws are in place.

Using our functors

A simple example of using our listFunctor that we defined in the preceding section can be seen as follows:

object FunctorsExample {
  def main(args: Array[String]): Unit = {
    val numbers = List(1, 2, 3, 4, 5, 6)
    val mapping = Map(
      1 -> "one", 
      2 -> "two",
      3 -> "three",
      4 -> "four",
      5 -> "five",
      6 -> "six"
    )
    
    System.out.println(s"The numbers doubled are: ${listFunctor.map(numbers)(_ * 2)}")
    System.out.println(s"The numbers with strings are: ${listFunctor.map(numbers)(i => (i, mapping(i)))}")
  }
}

The output of the preceding example is shown in the following screenshot:

Using our functors

As you can, functors don't really do much by themselves. They are not exciting at all. However, they set some specific rules that help us understand the results from specific operations. This means that we can define methods based on the abstract map inside the Functor trait, which rely on the rules we've stated previously.

Functors are an important concept that is required for monads, which we will look at in the next subsection.

..................Content has been hidden....................

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