首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >多态函数在Scala中是否具有“限制性”?

多态函数在Scala中是否具有“限制性”?
EN

Stack Overflow用户
提问于 2014-09-25 07:13:05
回答 3查看 391关注 0票数 13

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

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

并给出了实例

代码语言:javascript
复制
def partial1[A,B,C](a: A, f: (A,B) => C): B => C = (b: B) => f(a, b)

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

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-09-25 08:08:56

下面是一个简单的例子:

代码语言:javascript
复制
def mysteryMethod[A, B](somePair: (A, B)): B = ???

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

那么,它是做什么的?它接受一对(A, B)并返回一些类型为B的值。它返回的值是多少?它能构造一个B类型的值吗?不,它不能,因为它不知道什么是B!它能返回B类型的随机值吗?不,因为随机性是一种副作用,因此必须出现在类型签名中.它能在宇宙中出去取一些B吗?不,因为这将是一个副作用,必须出现在类型签名!

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

代码语言:javascript
复制
def second[A, B](somePair: (A, B)): B = somePair._2

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

但是,假设纯度(返回值可能只取决于参数)、总体(方法必须正常返回一个值)和参数性(它确实不知道AB),那么实际上可以通过查看方法的类型来了解它的很多信息。

下面是另一个例子:

代码语言:javascript
复制
def mysteryMethod(someBoolean: Boolean): Boolean = ???

这能做什么?它总是可以返回false并忽略它的参数。但是如果它总是忽略它的参数,那么它就不关心它是一个Boolean,它的类型应该是

代码语言:javascript
复制
def alwaysFalse[A](something: A): Boolean = false // same for true, obviously

它总是可以只返回它的论点,但同样的,它不会真的关心布尔人,它的类型应该是

代码语言:javascript
复制
def identity[A](something: A): A = something

所以,实际上,它唯一能做的就是返回一个与传入的布尔值不同的布尔值,因为只有两个布尔值,我们知道我们的mysteryMethod实际上是not

代码语言:javascript
复制
def not(someBoolean: Boolean): Boolean = if (someBoolean) false else true

所以,在这里,我们有一个例子,其中类型没有给我们实现,但至少,他们给出了一个(小)集4种可能的实现,其中只有一个是有意义的。

(顺便说一句:原来只有一个可能的方法实现,它接受一个A并返回一个A,这就是上面所示的标识方法。)

因此,简单地说:

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

将您的参数作为机器的一部分来考虑,将您的类型作为这些机器部件上的连接器。只有有限的几种方法可以将这些机器部件连接在一起,这样就只能将兼容的连接器插在一起,并且没有任何剩余的部件。通常情况下,只有一种方法,或者如果有多种方法,那么通常一种方式显然是正确的。

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

现在,看看问题中方法的签名,试着用不同的方式将af组合起来,使类型排列起来,同时使用af,您会发现只有一种方法可以做到这一点。(正如克里斯和保罗所展示的那样。)

票数 20
EN

Stack Overflow用户

发布于 2014-09-25 08:06:55

代码语言:javascript
复制
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应用于传递给partial的值a,以及类型B的值,它是我们返回的函数的参数。

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

票数 5
EN

Stack Overflow用户

发布于 2014-09-25 07:57:24

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

代码语言:javascript
复制
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是什么,所以您无法自己构建一个。因此,您创建的函数必须调用f才能获得C。为了调用f,您需要提供一个类型为AB的参数。同样,由于无法自己创建AB,所以您唯一能做的就是使用提供给您的参数。所以没有其他可能的函数,你可以写。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26032722

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档