我查询了参数化类与参数化函数之间的区别。
我提供了Functor的实现,如下所示:
trait Functor[F[_],A,B] {
def map(fa: F[A]) (f: A => B) : F[B]
}
和其他函数参数化的方法如下:
trait Functor[F[_]] {
def map[A,B](fa: F[A]) (f: A => B) : F[B]
}
我们应该在哪个地方使用另一个?
另一个跟进问题: 为什么我们将参数作为F [_]传递给仿函数而不是作为F [A]或F [B]。当我们使用F [A]或F [B]时会出现什么情况?
答案 0 :(得分: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)
}
即使你做得对,你也需要一个固定的函子实现每个不同类型的对象,你想要使用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?
}
此处的关键字是 parametricity 和参数多态。直觉是,如果通常定义某些东西,您可以从所涉及的类型中获得它将满足的属性。例如,请参阅Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free以获取更好的解释,或查看规范的Theorems for free论文。
另一个跟进问题:为什么我们将参数作为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)
}
现在关于如何与真实范畴理论联系起来:
上述Functor[F[_]]
特征的实例旨在代表 Scala - 丰富的仿函数
F: Scala → Scala
让我们解开这个。
有一个(通常是隐含定义的)类别 Scala ,包含对象类型,而morphisms函数f:A⇒B。这个类别是笛卡尔闭合的,其中内部hom是类型A⇒B,和产品(A,B)。我们可以使用 Scala - 丰富的类别和仿函数。什么是 Scala 丰富的类别?基本上你可以定义使用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]]
Scala 本身就是自然丰富的Scala[X,Y] = X => Y
和丰富的仿函数F: Scala → Scala 是什么实例你的Functor[F[_]]
特征是要代表的。
当然,这需要各种关于Scala如何打破这种情况的资格,以及态度平等等。但故事的寓意是:你的基本语言 L (在这种情况下就像Scala一样)是可能试图成为一个笛卡尔闭(或至少对称的幺半闭)类别,并且可以通过它定义的函子对应于 L - 丰富的函子。