Vimos una instancia de functor para Function1.
import cats.Functor
import cats.instances.function._ // for Functor
import cats.syntax.functor._
// for map
val func1 = (x: Int) => x.toDouble
val func2 = (y: Double) => y * 2
val func3 = func1.map(func2)
// func3: Int => Double = scala.Function1$$Lambda$7919/0
Function1 tiene dos parámetros de tipo (el argumento de la función y el tipo de resultado):
trait Function1[-A, +B] {
def apply(arg: A): B
}
Sin embargo, Functor acepta un constructor de tipos con un parámetro:
trait Functor[F[_]] {
def map[A, B](fa: F[A])(func: A => B): F[B]
}
El compilador tiene que corregir uno de los dos parámetros de Function1 para crear un constructor de tipos del tipo correcto para pasar a Functor. Tiene dos opciones a elegir:
type F[A] = Int => A
type F[A] = A => Double
Sabemos que la primera de ellas es la elección correcta. Sin embargo, el compilador no entiende lo que significa el código. En cambio, se basa en una regla simple, implementando lo que se llama "unificación parcial".
La unificación parcial en el compilador de Scala funciona fijando los parámetros de tipo de izquierda a derecha. En el ejemplo anterior, el compilador corrige Int en Int => Double y busca un Functor para funciones de tipo Int => ?:
type F[A] = Int => A
val functor = Functor[F]
Esta eliminación de izquierda a derecha funciona para una amplia variedad de escenarios comunes, incluidos Functors para tipos como Function1 y Either:
val either: Either[String, Int] = Right(123)
// either: Either[String, Int] = Right(123)
either.map(_ + 1)
// res0: Either[String, Int] = Right(124)