Scala中的多态函数是“限制性的”吗?

时间:2014-09-25 07:13:06

标签: scala polymorphic-functions

在Scala MEAP v10中的功能编程一书中,作者提到了

  

多态函数通常受类型约束,只有一个实现!

并给出了示例

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

这句话是什么意思?多态函数是否具有限制性?

3 个答案:

答案 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,并且它是上面显示的身份方法。)

所以,回顾一下:

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

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

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

现在,请查看问题中方法的签名,然后尝试使用不同的方法将Aa组合在一起,使类型排列并同时使用fa 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

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

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

现在,回到你的例子。你不知道C是什么,所以你无法自己构建一个。{1}}。因此,您创建的功能必须致电f才能获得C。要调用f,您需要提供类型AB的参数。同样,由于无法自己创建AB,您唯一能做的就是使用给您的参数。所以你没有其他可能写的功能。