8

Durante la lectura de la descripción de Functors en este blog:Scala - ¿Cómo usar Functors en tipos que no son de Función?

https://hseeberger.wordpress.com/2010/11/25/introduction-to-category-theory-in-scala/

hay una definición genérica de Functor y una más específica uno:

trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] { 
    def fmap[A, B](f: A ->> B): F[A] ->>> F[B] 
} 
trait Functor[F[_]] extends GenericFunctor[Function, Function, F] { 
    final def fmap[A, B](as: F[A])(f: A => B): F[B] = 
    fmap(f)(as) 
} 

Es evidente que esto significa Functors se puede utilizar con otro tipos de alto nivel además de objetos de Función. ¿Podría alguien dar un ejemplo o explicar cómo o por qué o en qué escenario se haría? A saber, ¿qué sería otra implementación de GenericFunctor en Scala, que utiliza un constructor de tipos diferente de Function? ¡Gracias!

EDIT:

Solo para aclarar:

object Functor { 

    def fmap[A, B, F[_]](as: F[A])(f: A => B)(implicit functor: Functor[F]): F[B] = 
    functor.fmap(as)(f) 

    implicit object ListFunctor extends Functor[List] { 
    def fmap[A, B](f: A => B): List[A] => List[B] = 
     as => as map f 
    } 
} 
scala> fmap(List(1, 2, 3))(x => x + 1) 
res0: List[Int] = List(2, 3, 4) 

Solo para aclarar, según mi entendimiento ListFunctor implementa el 1-arg fmap en GenericFunctor mientras que el código en la transcripción repl llama al fmap en Rasgo Functor, que a su vez llama a una implementación de fmap (por ejemplo, en ListFunctor).

Esto no cambia la pregunta general, solo pensé que ayudaría a las personas que intentaran dar respuestas. Cualquier información proporcionada sería apreciada.

Respuesta

7

En su ejemplo Functor es un endofunctor en la categoría de tipos de Scala con Function1 como flechas.

Hay otras categorías. Por ejemplo, imagine una categoría en la que los objetos son tipos Scala, y hay una flecha A >~> B si B es un subtipo de A. Esta categoría en Scalaz se llama Liskov.Hay un funtor "olvidadizo" de la categoría Liskov a la categoría Function1:

import scalaz._ 
import Scalaz._ 
trait Forget[F[-_]] extends GenericFunctor[>~>, Function1, F] { 
    def fmap[A, B](f: A >~> B): F[A] => F[B] = fa => f.subst(fa) 
} 

Tenga en cuenta que usted puede construir algunos funtores interesantes mediante la fijación de uno o más de los argumentos a GenericFunctor. Por ejemplo ...

Un funtor constante mapas de todos los objetos de una categoría a un solo objeto en otro:

type ConstantFunctor[->>[_, _], ->>>[_, _], C] = 
    GenericFunctor[->>,->>>,({type F[x] = C})#F] 
// def fmap[A, B](f: A ->> B): C ->>> C 

Un endofunctor asigna una categoría a la misma:

type EndoFunctor[->>[_, _], F[_]] = GenericFunctor[->>, ->>, F] 
// def fmap[A, B](f: A ->> B): F[A] ->> F[B] 

Un identificador de identidad mapea cada objeto y flecha a sí mismo:

type IdentityFunctor[->>[_, _]] = EndoFunctor[->>, ({type F[x] = x})#F] 
// def fmap[A, B](f: A ->> B): A ->> B 

Y, por supuesto, su Functor rasgo es sólo un EndoFunctor en la categoría Function1.

type Functor[F[_]] = EndoFunctor[Function1, F] 
// def fmap[A, B](f: A => B): F[A] => F[B] 
6

Se puede imaginar un funtor que eleva una instancia de Either[A,B] en un Either[F[A],F[B]] donde F puede ser un List, Option, etc.

EDITAR ejemplo Implementación:

trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] { 
    def fmap[A, B](f: A ->> B): F[A] ->>> F[B] 
} 

trait EitherFunctor[F[_]] extends GenericFunctor[Either,Either,F] 

object ListFunctor extends EitherFunctor[List] { 
    def fmap[A,B](f: Either[A,B]): Either[List[A],List[B]] = 
    f match { 
     case Left(a) => Left(List(a)) 
     case Right(b) => Right(List(b)) 
    } 
} 

Edit2 Otro (tal vez útil) ejemplo es con un functor con va desde PartialFunction (tipo ->>) a Function (tipo ->>>):

trait PartialFunctor[F[_]] 
extends GenericFunctor[PartialFunction,Function,F] { 
    final def fmap[A, B](as: F[A])(f: PartialFunction[A,B]): F[B] = 
    fmap(f)(as) 
} 

object OptionFunctor extends PartialFunctor[Option] { 
    def fmap[A,B](f: PartialFunction[A,B]): Option[A] => Option[B] = 
    (opt:Option[A]) => opt match { 
     case Some(a) => f.lift(a) 
     case None => None 
    } 
} 

object ListFunctor extends PartialFunctor[List] { 
    private def mapPartial[A,B](f: PartialFunction[A,B], as: List[A]): List[B] = 
    as match { 
     case Nil => Nil 
     case h :: t => if(f isDefinedAt h) f(h) :: mapPartial(f, t) 
        else mapPartial(f, t) 
    } 

    def fmap[A,B](f: PartialFunction[A,B]): List[A] => List[B] = 
    (lst:List[A]) => mapPartial(f, lst) 

} 

Este segundo ejemplo permite implementar la operación collect como se ha definido en colecciones Scala:

def collect[A,B,F[_]](as: F[A]) 
        (pf: PartialFunction[A,B]) 
        (implicit functor: PartialFunctor[F]) = 
    functor.fmap(as)(pf) 
+0

No creo que the-functor sea un functor. No veo cómo implementarías una categoría. – Anonymous

Cuestiones relacionadas