首页 > 解决方案 > 高级 C[_] ~> D[_] 的多态函数真的是自然变换吗?

问题描述

这是一个关于术语的问题,在 scala shapeless 库中我发现了以下评论:

  /**
   * Base trait for natural transformations.
   *
   * @author Miles Sabin
   */
  trait ~>[F[_], G[_]] extends Poly1 {

这定义F[_] ~> G[_]为自然转换。但我不确定它是否在滥用术语,在维基百科中:

https://en.wikipedia.org/wiki/Natural_transformation

它被定义为函子之间的结构保持态射。例如对于函数的底层函子Int => String

v: Int => v.toString

自然转换可以将其转换为另一个仿函数,例如List[Int] => List[String]orSet[Int] => Set[String]等​​。这似乎表明 scala 中最接近自然转换的对象实际上是CanBuildFrom集合库中的类型类:

trait CanBuildFrom[-From, -Elem, +To]

...

object Example extends CanBuildFrom[List[Int], Int, List[String]]

其中(在柯里化之后)是从一个函子F[_]: Elem => From到另一个函子的态射G[_]: Elem => To。据我所知,由 定义的 Poly1~>不具备这种能力(采用任何函子 X => Y 并产生 List[X] => List[Y]),那么为什么将其称为自然变换呢?

UPDATE 1,我想我可能需要澄清一下。一些库(如猫)中的函子定义仅包含类型构造函数,例如:

T => List[T]

但是根据数学定义,仿函数应该类似于 lambda 类型,例如以下也应该是仿函数

T => String
T => List[String] // in that CanBuildFrom example

我的问题是,从这个意义上说,类型类CanBuildFrom应该是自然转换。然而,在无形中,除非我使用临时多态性(Poly1 主体中的多个隐含),否则我无法定义它。那么这究竟~>意味着什么呢?

非常感谢您的意见

标签: scalagenericspolymorphismshapeless

解决方案


范畴论在编程语言中并不总是可以 1-1 实现。例如函子。当你有F允许翻译A => BF[A] => F[B]它的数据结构时,它就是一个函子。但是,如果您使用协方差F[+A],这A <: B意味着F[A] <: F[B]您也有一个函子,但在类型级别上。

没有一个结构可以让您描述仿函数的所有用法。从理论上讲,如果你有一些F[A] => F[B]可以翻译成G[A] => G[B]的东西,并且你可以组成的所有东西都F应该有一个对应的组成 in G,那么你就有了一个函子。但是你不能F => G为所有东西生成这样的函数。如果你从 开始,你可以很容易地生成这样的映射Id,所以仅仅因为方便,所有描述函子的类型类只描述一个映射,它从“forever”由泛型处理的地方开始Id[A]。这只是一些价值层面的函子——还有类型上的函子。或者在任何其他多重图上,例如,您可以定义枚举中某些值之间的转换F[A]AE1然后对另一个 enum 做同样的事情E2,如果一个是另一个的子图并且你提供了一个映射(一些受约束的(E1, E1)=> (E2, E2)) - 这也是一个仿函数。你不会只用一个通用的[A]. 如果你专门为这些枚举实现函子,Functor接口的通用性和可组合性不会有任何好处。它只是一个恰好描述函子的函数,那又如何呢?

这会影响自然转换。实际上,我们在 Cats 或类似中描述的唯一自然变换是Id[A] => F[A]和之间的自然变换Id[A] => G[A]。它们很容易实现,因为您基本上是F[A] => G[A]Id[A] => F[A]获取Id[A] => G[A]. 在一般情况下并不容易。

这些仍然是函子和自然转换。只是为了我们的实际目的,我们只考虑函子和 NT,Id因为我们可以经常、轻松且廉价地创建它们。泛型/参数类型免费为您提供映射A=> F[A]A我们只是在此基础上构建。您可以开始使用~>for 仿函数,然后创建一个处理此类仿函数(自然转换)的仿函数……但您很快会发现,像(A ~> B) ~> (C ~> D)while 模型这样的好主意,使用起来也非常不切实际。数学中的许多概念也是如此。我们没有完美地建模它们,而只是使用在特定上下文中对我们有用的特定专业化。


推荐阅读