参数化类与函数

Mau*_*eji 2 generics scala functor typeclass category-theory

我查询了参数化类与参数化函数之间的区别.

我提供了一个Functor的实现,如下所示:

trait Functor[F[_],A,B] {
  def map(fa: F[A]) (f: A => B) : F[B]
}
Run Code Online (Sandbox Code Playgroud)

以及其他函数参数化的方法如下:

trait Functor[F[_]] {
  def map[A,B](fa: F[A]) (f: A => B) : F[B]
}
Run Code Online (Sandbox Code Playgroud)

在哪里我们应该使用一个而不是另一个?

另一个跟进问题:为什么我们将参数作为F [_]而不是F [A]或F [B]传递给仿函数.当我们使用F [A]或F [B]时会出现什么情况?

Edu*_*bes 6

总是喜欢第二个.使用第一个实例,您可以将实例实现为无意义的:

trait WrongFunctor[F[_],A,B] {
  def map(fa: F[A])(f: A => B) : F[B]
}

case class notEvenRemotelyAFunctor[A]() extends WrongFunctor[List,A,Int] {

  def map(fa: List[A])(f: A => Int) : List[Int] = 
    if(f(fa.head) < 4) List(3) else List(4)
}

type Id[X] = X
case object ILikeThree extends WrongFunctor[Id, Int, Int] {

  def map(fa: Int)(f: Int => Int): Int = if(fa == 3) 3 else f(fa)
}
Run Code Online (Sandbox Code Playgroud)

即使你做得对,你也需要一个固定的函子实现一个对象,每个不同的类型你想要使用它fmap.但重要的是,第二个问题至少使编写那种错误的"仿函数"变得更加困难; 较少的非仿函数会滑过:

trait Functor[F[_]] {

  def map[A,B](fa: F[A])(f: A => B) : F[B]
}
case object ILikeThreeAgain extends Functor[Id] {

  def map[A,B](fa: A)(f: A => B) : B =
    ??? // how do I write the above here?
}
Run Code Online (Sandbox Code Playgroud)

这里的关键字是参数化参数多态.直觉是,如果通常定义某些东西,您可以从所涉及的类型中获得它将满足的属性.例如,请参阅Bartosz Milewski博客 - Parametricity:Money for Nothing和免费定理,以获得更好的解释,或者免费论文的规范定理.

后续问题

另一个跟进问题:为什么我们将参数作为F [_]而不是F [A]或F [B]传递给仿函数.当我们使用F [A]或F [B]时会出现什么情况?

因为那是仿函数的一部分; 它是一个"构造函数":

  1. 对于每种输入类型,A它为您提供另一种类型的输出F[A]
  2. 对于每个函数f: A => B,另一个函数fmap(f): F[A] => F[B]满足fmap(id[A]) == id[F[A]]fmap(f andThen g) == fmap(f) andThen fmap(g)

所以对于1.你需要一种类型上表示函数的方法; 那是什么F[_].

请注意,map在这种情况下,使用签名中的方法相当于fmap:

trait Functor[F[_]] {

  def map[A,B](fa: F[A])(f: A => B) : F[B]

  def fmap[A,B](f: A => B): F[A] => F[B] =
    { fa => map(fa)(f) }

  def mapAgain[A,B](fa: F[A])(f: A => B) : F[B] =
    fmap(f)(fa)
}
Run Code Online (Sandbox Code Playgroud)

现在,这与真正的类别理论如何联系:

Functor[F[_]]上面的特征实例用于表示Scala -enriched仿函数

F: 斯卡拉斯卡拉

我们打开这个.

有一个(通常是隐式定义的)类别Scala与对象类型,和态射函数f:A⇒B.这个类别是笛卡尔闭合,其中内部hom是A⇒B 和产品(A,B).我们可以使用Scala -enriched类和functor工作.什么是Scala -enriched类别?基本上你可以用Scala 定义一种语言:你有

  1. 一组对象(您需要将其表示为类型)
  2. 对于每个A,B,具有满足类别公理的C[A,B]身份id[X]: C[X,Y]和组成andThen[X,Y,Z]: (C[X,Y], C[Y,Z]) => C[X,Z]的类型

丰富的仿函数F:C→D然后

  1. 从C的对象到D的对象的函数, A -> F[A]
  2. 对于每对物体A,B:C在态射的Scala即函数fmap: C[A,B] => C[F[A], F[B]]满足算符法律fmap(id[A]) == id[F[A]]fmap(f andThen g) == fmap(f) andThen fmap(g)

Scala本身就是自然丰富的Scala[X,Y] = X => Y,并且有丰富的仿函数F:ScalaScala是你的Functor[F[_]]特征的实例所代表的.

当然,这需要各种关于Scala如何打破这种情况以及态度平等等的资格.但故事的寓意是:你的基础语言L(在这种情况下像Scala)很可能试图成为一个笛卡尔式的(或者至少对称的monoidal-closed)类别,以及通过它定义的functor对应于L -enriched functor.