在 Scala MEAP v10 中的函数式编程一书中,作者提到
多态函数通常受其类型的限制,以至于它们只有一种实现!
并给出了例子
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)
他这句话是什么意思?多态函数有限制吗?
在 Scala MEAP v10 中的函数式编程一书中,作者提到
多态函数通常受其类型的限制,以至于它们只有一种实现!
并给出了例子
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)
他这句话是什么意思?多态函数有限制吗?
这是一个更简单的例子:
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 上有多少问题基本上是“我如何实现这个?”,你能想象它必须如何释放它根本不必考虑这一点,因为类型已经决定了一种(或少数几种)可能的实现?
现在,查看您的问题中方法的签名,并尝试使用不同的方式进行组合a
,f
以使类型对齐并且您使用两者a
,f
并且您确实会看到只有一种方法可以做到这一点. (正如克里斯和保罗所展示的那样。)
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 的值。a
partial
所以你最终得到了定义中的单一可能性f(a,b)
举一个更简单的例子,考虑 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
前两个选择几乎相同,而后两个是微不足道的,所以基本上这个函数只能做一件有用的事情,那就是告诉你Option
isSome
还是None
.
可能实现的空间受到函数类型的抽象性的“限制”。由于A
不受约束,因此选项的值可以是任何值,因此该函数不能以任何方式依赖该值,因为您对它一无所知。函数可能对其参数的唯一“理解”是 的结构Option[_]
。
现在,回到你的例子。你不知道是什么C
,所以你无法自己构建一个。因此,您创建的函数将不得不调用f
以获取C
. 为了调用f
,您需要提供类型A
和参数B
。同样,由于无法自己创建 anA
或 a B
,您唯一能做的就是使用提供给您的参数。因此,您无法编写其他可能的功能。