Translate

lunes, 18 de abril de 2022

Funtores en Cats parte 4

Podemos definir un funtor simplemente definiendo su método map. Aquí hay un ejemplo de un Functor para Option, aunque tal cosa ya existe en cats.instances. La implementación es trivial: simplemente llamamos al método de mapa de Option:


implicit val optionFunctor: Functor[Option] = new Functor[Option] {

   def map[A, B](value: Option[A])(func: A => B): Option[B] = value.map(func)

}

A veces necesitamos inyectar dependencias en nuestras instancias. Por ejemplo, si tuviéramos que definir un Functor personalizado para Future (otro ejemplo hipotético: Cats proporciona uno en cats.instances.future), tendríamos que tener en cuenta el parámetro ExecutionContext implícito en future.map. No podemos agregar parámetros adicionales a functor.map, por lo que debemos tener en cuenta la dependencia cuando creamos la instancia:

import scala.concurrent.{Future, ExecutionContext}

implicit def futureFunctor(implicit ec: ExecutionContext): Functor[Future] = new Functor[Future] {
  def map[A, B](value: Future[A])(func: A => B): Future[B] = value.map(func)
}

Cada vez que llamamos a un Functor for Future, ya sea directamente usando Functor.apply o indirectamente a través del método de extensión map, el compilador ubicará futureFunctor por resolución implícita y buscará recursivamente un ExecutionContext en el sitio de la llamada. Así es como se vería la expansión:

// We write this:
Functor[Future]

// The compiler expands to this first:
Functor[Future](futureFunctor)

// And then to this:
Functor[Future](futureFunctor(executionContext))