参数化 Class 与函数
Parameterised Class vs Function
我想问一下 class 参数化与函数参数化之间的区别。
我提供了一个 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] 时会出现什么情况?
总是更喜欢第二个。对于第一个,您可以将实例实现为无意义的:
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
。但是重要的一点是,第二个至少让写那种错误变得更难了"functors";更少的非函数者会溜走:
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和parametric polymorphism。直觉是,如果某些东西是通用定义的,您可以从所涉及的类型中派生出它将满足的属性。例如,参见 Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free for a good explanation, or the canonical Theorems for free 论文。
跟进问题
Another follow up question: Why do we pass the argument to functor as F[_] and not as F[A] or F[B]. What cases arise when we use either F[A] or F[B]?
因为这是仿函数的一部分;这是一个 "constructor":
- 对于每种输入类型
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. 你需要一种表示函数 on 类型的方法;这就是 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-enriched 仿函数
F: Scala → Scala
让我们打开包装。
有一个(通常是隐式定义的)范畴 Scala 具有对象类型和态射函数 f: A ⇒ B。这个范畴是笛卡尔闭的,其中内部 hom 是 type A ⇒ B,和乘积 (A,B)。然后我们可以使用 Scala 丰富的类别和仿函数。什么是 Scala 丰富的类别?基本上,您可以使用 Scala 语言定义:您有
- 一组对象(您需要将其表示为类型)
- 对于每个 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 是
- 从C的对象到D的对象的函数,
A -> F[A]
- 对于每对对象 A,B:C 在 Scala 中的态射,即满足函子定律
fmap(id[A]) == id[F[A]]
和 [=19 的函数 fmap: C[A,B] => C[F[A], F[B]]
=]
Scala本身自然丰富,有Scala[X,Y] = X => Y
,丰富了函子F:Scala→Scala 是你的 Functor[F[_]]
特质的实例。
当然,这需要关于 Scala 如何打破这个和那个、态射相等等的各种限定条件。但这个故事的寓意是:你的基础语言 L(就像 Scala在这种情况下)很可能试图成为笛卡尔闭(或至少对称幺半群闭)类别,并且通过它定义的函子对应于 L-enriched 函子。
我想问一下 class 参数化与函数参数化之间的区别。
我提供了一个 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] 时会出现什么情况?
总是更喜欢第二个。对于第一个,您可以将实例实现为无意义的:
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
。但是重要的一点是,第二个至少让写那种错误变得更难了"functors";更少的非函数者会溜走:
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和parametric polymorphism。直觉是,如果某些东西是通用定义的,您可以从所涉及的类型中派生出它将满足的属性。例如,参见 Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free for a good explanation, or the canonical Theorems for free 论文。
跟进问题
Another follow up question: Why do we pass the argument to functor as F[_] and not as F[A] or F[B]. What cases arise when we use either F[A] or F[B]?
因为这是仿函数的一部分;这是一个 "constructor":
- 对于每种输入类型
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. 你需要一种表示函数 on 类型的方法;这就是 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-enriched 仿函数
F: Scala → Scala
让我们打开包装。
有一个(通常是隐式定义的)范畴 Scala 具有对象类型和态射函数 f: A ⇒ B。这个范畴是笛卡尔闭的,其中内部 hom 是 type A ⇒ B,和乘积 (A,B)。然后我们可以使用 Scala 丰富的类别和仿函数。什么是 Scala 丰富的类别?基本上,您可以使用 Scala 语言定义:您有
- 一组对象(您需要将其表示为类型)
- 对于每个 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 是
- 从C的对象到D的对象的函数,
A -> F[A]
- 对于每对对象 A,B:C 在 Scala 中的态射,即满足函子定律
fmap(id[A]) == id[F[A]]
和 [=19 的函数fmap: C[A,B] => C[F[A], F[B]]
=]
Scala本身自然丰富,有Scala[X,Y] = X => Y
,丰富了函子F:Scala→Scala 是你的 Functor[F[_]]
特质的实例。
当然,这需要关于 Scala 如何打破这个和那个、态射相等等的各种限定条件。但这个故事的寓意是:你的基础语言 L(就像 Scala在这种情况下)很可能试图成为笛卡尔闭(或至少对称幺半群闭)类别,并且通过它定义的函子对应于 L-enriched 函子。