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

感情迁移 提交于 2019-12-03 12:49:14

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]

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 )

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