13

在 Scala MEAP v10 中的函数式编程一书中,作者提到

多态函数通常受其类型的限制,以至于它们只有一种实现!

并给出了例子

def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)

他这句话是什么意思?多态函数有限制吗?

4

3 回答 3

20

这是一个更简单的例子:

def mysteryMethod[A, B](somePair: (A, B)): B = ???

这个方法有什么作用?事实证明,这种方法只能做件事!你不需要方法的名字,你不需要方法的实现,你不需要任何文档。类型告诉你它可能做的一切,事实证明,在这种情况下,“一切”只是一件事

那么,它有什么作用呢?它需要一对(A, B)并返回一些类型的值B。它返回什么值?它可以构造一个类型的值B吗?不,它不能,因为它不知道是什么B!它可以返回类型的随机值B吗?不,因为随机性是一种副作用,因此必须出现在类型签名中。它可以在宇宙中出去取一些B吗?不,因为将是一个副作用,并且必须出现在类型签名中!

事实上,它唯一能做的就是返回B传递给它的类型的值,即对的第二个元素。所以,这mysteryMethod确实是second方法,它唯一合理的实现是:

def second[A, B](somePair: (A, B)): B = somePair._2

请注意,实际上,由于 Scala 既不是纯粹的也不是完全的,实际上该方法还可以做一些其他事情:抛出异常(即异常返回),进入无限循环(即根本不返回),使用反射以找出实际类型B并反射性地调用构造函数以制造新值等。

但是,假设纯度(返回值可能仅取决于参数)、整体性(方法必须正常返回值)和参数性(它真的A对and什么都不知道B),那么实际上你可以只看它的类型来讲述一个方法。

这是另一个例子:

def mysteryMethod(someBoolean: Boolean): Boolean = ???

这能做什么?它总是可以返回false并忽略它的论点。但是它会被过度约束:如果它总是忽略它的论点,那么它就不关心它是 aBoolean并且它的类型宁愿是

def alwaysFalse[A](something: A): Boolean = false // same for true, obviously

它总是可以只返回它的参数,但同样,它实际上并不关心布尔值,它的类型宁愿是

def identity[A](something: A): A = something

所以,真的,它唯一能做的就是返回一个与传入的布尔值不同的布尔值,并且由于只有两个布尔值,我们知道我们的神秘方法实际上是not

def not(someBoolean: Boolean): Boolean = if (someBoolean) false else true

因此,在这里,我们有一个示例,其中类型没有为我们提供实现,但至少,它们作为(小)集提供了 4 种可能的实现,其中只有一个是有意义的。

(顺便说一句:事实证明,只有一种可能的实现方法,它接受 anA并返回 an A,它是上面显示的标识方法。)

所以,回顾一下:

  • 纯度意味着您只能使用交给您的构建块(参数)
  • 一个强大的、严格的、静态的类型系统意味着您只能以它们的类型排列的方式使用这些构建块
  • 整体意味着你不能做愚蠢的事情(比如无限循环或抛出异常)
  • 参数化意味着你不能对你的类型变量做任何假设

将您的论点视为机器的一部分,将您的类型视为这些机器部件上的连接器。只有有限数量的方法可以将这些机器部件连接在一起,而您只需将兼容的连接器插入在一起,并且没有任何剩余部件。很多时候,只有一种方式,或者如果有多种方式,那么通常一种显然是正确的。

这意味着,一旦您设计了对象和方法的类型,您甚至不必考虑如何实现这些方法,因为类型已经决定了实现它们的唯一可能方式!考虑到 StackOverflow 上有多少问题基本上是“我如何实现这个?”,你能想象它必须如何释放它根本不必考虑这一点因为类型已经决定了一种(或少数几种)可能的实现?

现在,查看您的问题中方法的签名,并尝试使用不同的方式进行组合af以使类型对齐并且您使用两者af并且您确实会看到只有一种方法可以做到这一点. (正如克里斯和保罗所展示的那样。)

于 2014-09-25T08:08:56.457 回答
5
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)

这里,partial1将 A 类型的值作为参数,一个函数将 A 类型的参数和 B 类型的参数作为参数,返回 C 类型的值。

partial1必须返回一个接受 B 类型值并返回 C 的函数。鉴于 A、B 和 C 是任意的,我们不能将任何函数应用于它们的值。因此,唯一的可能性是将函数应用于传递给f的值,以及作为我们返回函数的参数的类型 B 的值。apartial

所以你最终得到了定义中的单一可能性f(a,b)

于 2014-09-25T08:06:55.740 回答
3

举一个更简单的例子,考虑 type Option[A] => Boolean。只有几种方法可以实现这一点:

def foo1(x: Option[A]): Boolean = x match { case Some(_) => true
                                            case None    => false }
def foo2(x: Option[A]): Boolean = !foo1(x)
def foo3(x: Option[A]): Boolean = true
def foo4(x: Option[A]): Boolean = false

前两个选择几乎相同,而后两个是微不足道的,所以基本上这个函数只能做一件有用的事情,那就是告诉你OptionisSome还是None.

可能实现的空间受到函数类型的抽象性的“限制”。由于A不受约束,因此选项的值可以是任何值,因此该函数不能以任何方式依赖该值,因为您对它一无所知。函数可能对其参数的唯一“理解”是 的结构Option[_]

现在,回到你的例子。你不知道是什么C,所以你无法自己构建一个。因此,您创建的函数将不得不调用f以获取C. 为了调用f,您需要提供类型A和参数B。同样,由于无法自己创建 anA或 a B,您唯一能做的就是使用提供给您的参数。因此,您无法编写其他可能的功能。

于 2014-09-25T07:57:24.217 回答