在Scala MEAP v10中的功能编程一书中,作者提到了
多态函数通常受类型约束,只有一个实现!
并给出了示例
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)
这句话是什么意思?多态函数是否具有限制性?
答案 0 :(得分: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
的实际类型,并反射调用构造函数来构造新值等。
但是,假设纯度(返回值可能仅取决于参数),totality(方法必须正常返回值)和参数(真的对{不知道' {1}}和A
),实际上有很多关于仅查看其类型的方法的信息很多。
这是另一个例子:
B
这可以做什么?它总是可以返回def mysteryMethod(someBoolean: Boolean): Boolean = ???
并忽略它的参数。但是它会受到过度约束:如果它总是忽略它的论点,那么它并不关心它是false
而它的类型宁愿是
Boolean
它总是可以只返回它的论点,但是再一次,那么它实际上并不关心布尔值,它的类型宁愿是
def alwaysFalse[A](something: A): Boolean = false // same for true, obviously
所以,真的,它唯一能做的就是返回一个不同的布尔值而不是传入的布尔值,因为只有两个布尔值,我们知道我们的mysteryMethod实际上是,def identity[A](something: A): A = something
:
not
所以,在这里,我们有一个例子,其中的类型没有给我们 实现,但至少,它们作为(小)一组4个可能的实现给出,仅其中一个是有道理的。
(顺便说一下:事实证明,只有一种方法的可能实现,它采用def not(someBoolean: Boolean): Boolean = if (someBoolean) false else true
并返回A
,并且它是上面显示的身份方法。)
所以,回顾一下:
将您的参数视为机器的一部分,将您的类型视为这些机器部件上的连接器。只有有限的方法可以将这些机器部件连接在一起,只需将兼容的连接器插在一起,而且没有任何剩余的部件。通常,只有一种方式,或者如果有多种方式,那么通常一种方式显然是正确的。
这意味着,一旦您设计了对象和方法的类型,您甚至不必思考关于如何实现这些方法,因为类型已经决定实施它们的唯一可能方式!考虑到StackOverflow上有多少问题基本上是"我该如何实现这个?",你能想象它是如何解放它必须不必考虑所有,因为类型已经规定了一个(或几个)可能的实施?
现在,请查看问题中方法的签名,然后尝试使用不同的方法将A
和a
组合在一起,使类型排列并同时使用f
和a
1}}和f
你确实会发现只有一种方法可以做到这一点。 (正如克里斯和保罗所示。)
答案 1 :(得分: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
应用于传递给a
的值partial
,以及作为我们返回的函数的参数的类型B的值。
所以你最终得到了f(a,b)
答案 2 :(得分:3)
举一个更简单的例子,考虑类型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
是Some
还是None
。
可能实现的空间受到函数类型抽象性的“限制”。由于A
不受约束,因此选项的值可以是任何值,因此函数不能以任何方式依赖于该值,因为您对它的含义一无所知。函数对其参数的唯一“理解”是Option[_]
的结构。
现在,回到你的例子。你不知道C
是什么,所以你无法自己构建一个。{1}}。因此,您创建的功能必须致电f
才能获得C
。要调用f
,您需要提供类型A
和B
的参数。同样,由于无法自己创建A
或B
,您唯一能做的就是使用给您的参数。所以你没有其他可能写的功能。