Scala - 如何在非函数类型上使用Functors?

时间:2011-09-27 15:46:47

标签: function scala functional-programming functor category-theory

在阅读此博客上的Functors说明时:

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

有一个Functor的通用定义和一个更具体的定义:

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

显然,这意味着Functors可以与除Function对象之外的其他更高级别的类型一起使用。有人可以举一个例子或解释如何或为什么或在什么情况下做?也就是说,GenericFunctor的另一个实现是什么?在Scala中 - 使用函数中的不同类型构造函数?谢谢!

编辑:

只是为了澄清:

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)

只是为了澄清,根据我的理解,ListFunctor在GenericFunctor中实现1-arg fmap,而repl transcript中的代码在Trait Functor中调用fmap,后者又调用fmap实现(例如在ListFunctor中)。

这并没有改变整体问题,只是认为它会帮助人们尝试提供答案。任何见解都将不胜感激。

3 个答案:

答案 0 :(得分:7)

在您的示例中,Functor是Scala类型中的endofunctor,其中Function1为箭头。

还有其他类别。例如,假设一个对象是Scala类型的类别,如果A >~> BB的子类型,则有一个箭头AScalaz中的此类别称为Liskov。从Liskov类别到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)
}

请注意,您可以通过将一个或多个参数修改为GenericFunctor来构建一些有趣的仿函数。例如......

常量仿函数将一个类别中的每个对象映射到另一个类别中的单个对象:

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

endofunctor 将类别映射到自身:

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

身份仿函数将每个对象和箭头映射到自身:

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

当然,您的Functor特征只是EndoFunctor类别中的Function1

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

答案 1 :(得分:6)

您可以想象一个将Either[A,B]的实例提升为Either[F[A],F[B]]的仿函数,其中F可以是ListOption等。

编辑实施示例:

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 另一个(可能有用的)示例是使用仿函数,从PartialFunction(类型->>)到Function(类型{{1} }}):

->>>

第二个示例允许实现Scala集合中定义的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) } 操作:

collect

答案 2 :(得分:4)

Data.Category是这类事物(在Haskell中)的一个很好的来源。从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)
}