gpt4 book ai didi

scala - 参数化类与函数

转载 作者:行者123 更新时间:2023-12-02 08:17:25 25 4
gpt4 key购买 nike

我想知道类参数化与函数参数化之间的区别是什么。

我提供了一个 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。但重要的一点是,第二个至少使编写那种错误的“仿函数”变得更加困难;更少的 non-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?
}

这里的关键词是parametricityparametric polymorphism。直觉是,如果某些东西是通用定义的,您可以从所涉及的类型中派生出它将满足的属性。参见示例 Bartosz Milewski blog - Parametricity: Money for Nothing and Theorems for Free一个很好的解释,或规范 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]?

因为这是仿函数的一部分;它是一个“构造函数”:

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

现在看看这与真实范畴论的联系:

上面的 Functor[F[_]] trait 的实例是为了表示 Scala-enriched 仿函数

F: ScalaScala

让我们打开包装。

有一个(通常是隐式定义的)范畴 Scala 具有对象类型和态射函数 f:A ⇒ B。这个范畴是笛卡尔闭的,其中内部 hom 是类型 A ⇒ B,以及乘积 (A,B)。然后我们可以使用 Scala 丰富的类别和仿函数。什么是 Scala 丰富的类别?基本上您可以使用 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)是可能试图成为笛卡尔闭(或至少对称幺半群闭)范畴,并且可通过它定义的仿函数对应于 L-enriched 仿函数。

关于scala - 参数化类与函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40705646/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com