8

Leggendo la descrizione di Funtori su questo blog:Scala - Come utilizzare i Functional su tipi non funzionali?

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

v'è una definizione generica di Functor e uno più specifico:

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) 
} 

Chiaramente questo significa Funtori può essere utilizzato con altri tipi di tipo superiore oltre a oggetti Function. Qualcuno potrebbe dare un esempio o spiegare come o perché o in quale scenario si farebbe? Vale a dire, quale sarebbe un'altra implementazione di GenericFunctor in Scala - che utilizza un costruttore di tipo diverso da Function? Grazie!

EDIT:

Giusto per chiarire:

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 per chiarire, secondo la mia comprensione ListFunctor implementa l'1-arg FMAP nel GenericFunctor mentre il codice nella trascrizione repl chiama il FMAP in Trait Functor, che a sua volta chiama un'implementazione di fmap (ad es. In ListFunctor).

Questo non modifica la domanda generale, pensavo solo che avrebbe aiutato le persone a cercare di fornire risposte. Qualsiasi intuizione fornita sarebbe apprezzata.

risposta

7

Nel tuo esempio Functor è un endofunctor nella categoria dei tipi Scala con Function1 come frecce.

Ci sono altre categorie. Ad esempio, immagina una categoria in cui gli oggetti sono tipi Scala e c'è una freccia A >~> B se B è un sottotipo di A. Questa categoria in Scalaz è denominata Liskov.C'è un funtore "smemorato" dalla categoria Liskov alla categoria 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) 
} 

noti che è possibile costruire alcuni funtori interessanti fissando uno o più argomenti per GenericFunctor. Per esempio ...

Un costante functor mappe ogni oggetto in una categoria ad un singolo oggetto in un altro:

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

Un endofunctor mappe una categoria a se stesso:

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

Un identificatore identità esegue il mapping di ogni oggetto e freccia a se stesso:

E, naturalmente, il tuo tratto Functor è solo un EndoFunctor nella categoria Function1.

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

È possibile immaginare un funtore che solleva un'istanza di Either[A,B] in un Either[F[A],F[B]] dove F possono essere un List, Option, ecc

EDIT Attuazione esempio:

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 Un'altra (forse utile) esempio è con un functor con va da un PartialFunction (tipo ->>) a un 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) 

} 

Questo secondo esempio consente di attuare l'operazione collect come definito in collezioni Scala:

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

Non penso che l'uno o l'altro sia un funtore. Non riesco a vedere come implementeresti una delle due categorie. – Anonymous