Scala — How to use Functors on non-Function types?

回眸只為那壹抹淺笑 提交于 2019-12-21 04:37:12

问题


While reading the description of Functors on this blog:

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

there is a generic definition of Functor and a more specific one:

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

Clearly this means Functors can be used with other higher-kinded types besides Function objects. Could someone please give an example or explain how or why or in what scenario that would be done? Namely, what would another implementation of GenericFunctor be in Scala -- that uses a different type constructor from Function? Thanks!

EDIT:

Just to clarify:

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)

Just to clarify, according to my understanding ListFunctor implements the 1-arg fmap in GenericFunctor while the code in the repl transcript calls the fmap in Trait Functor, which in turn calls an fmap implementation (e.g. in ListFunctor).

This doesn't change the overall question, just thought it would help people trying to provide answers. Any insights provided would be appreciated.


回答1:


In your example Functor is an endofunctor in the category of Scala types with Function1 as arrows.

There are other categories. For example, imagine a category in which the objects are Scala types, and there is an arrow A >~> B if B is a subtype of A. This category in Scalaz is called Liskov. There is a "forgetful" functor from the Liskov category to the Function1 category:

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

Note that you can build some interesting functors by fixing one or more of the arguments to GenericFunctor. For example...

A constant functor maps every object in one category to a single object in another:

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

An endofunctor maps a category to itself:

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

An identity functor maps every object and arrow to itself:

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

And of course, your Functor trait is just an EndoFunctor in the Function1 category.

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



回答2:


You can imagine a functor which lifts an instance of Either[A,B] into an Either[F[A],F[B]] where F can be a List, Option, etc.

EDIT Implementation example:

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 Another (maybe useful) example is with a functor with goes from a PartialFunction (type ->>) to a Function (type ->>>):

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)

}

This second example allows to implement the collect operation as defined in Scala collections:

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



回答3:


Data.Category is a good source for examples of things like this (in Haskell). Partially translating one of the instances from http://hackage.haskell.org/packages/archive/data-category/0.4.1/doc/html/src/Data-Category-Functor.html:

type OpFun[A, B] = B => A

case class OpFunctor[F[_]](functor: Functor[F[_]]) extends GenericFunctor[OpFun, OpFun, F] {
  def fmap[A, B](f: B => A): F[B] => F[A] = fb => functor.fmap(fb)(f)
}


来源:https://stackoverflow.com/questions/7571959/scala-how-to-use-functors-on-non-function-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!