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]时会出现什么情况?
总是喜欢第二个.使用第一个实例,您可以将实例实现为无意义的:
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]时会出现什么情况?
因为那是仿函数的一部分; 它是一个"构造函数":
A它为您提供另一种类型的输出F[A]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 定义一种语言:你有
C[A,B]身份id[X]: C[X,Y]和组成andThen[X,Y,Z]: (C[X,Y], C[Y,Z]) => C[X,Z]的类型丰富的仿函数F:C→D然后
A -> F[A]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:Scala → Scala是你的Functor[F[_]]特征的实例所代表的.
当然,这需要各种关于Scala如何打破这种情况以及态度平等等的资格.但故事的寓意是:你的基础语言L(在这种情况下像Scala)很可能试图成为一个笛卡尔式的(或者至少对称的monoidal-closed)类别,以及通过它定义的functor对应于L -enriched functor.