除了零参数方法之外,为什么Scala需要无参数?

Lui*_*hys 33 scala

我理解零参数和无参数方法之间的区别,但我真正理解的是语言设计选择,这使得无参数方法成为必要.

我能想到的缺点:

  • 这令人困惑.每隔一两周,这里或Scala邮件列表上都有关于它的问题.
  • 情况很复杂; 我们还要区分() => X=> X.
  • 这是不明确的:它的x.toFoo(y)意思是什么,或者x.toFoo.apply(y)?(答:这要看是什么重载还有xtoFoo方法和重载Fooapply方法,但如果有冲突,你不会看到一个错误,直到您尝试调用它.)
  • 它混淆了操作符样式方法调用语法:链接方法时,没有符号可用于代替参数,或者最后避免使用分号干扰.使用零参数方法,您可以使用空参数列表().

目前,您不能在类中定义两者:您会收到一条错误消息,说明该方法已定义.他们也都转换为Function0.

为什么不只是制作方法def foodef foo()完全相同的东西,并允许使用括号或不使用括号来调用它们?它的优点是什么?

nad*_*vwr 28

Currying,这就是为什么

Daniel在解释为什么无参数方法是必要的方面做得很好.我将解释为什么它们与零参数方法明显不同.

许多人将无参数函数和零参数函数之间的区别视为一种模糊形式的句法糖.事实上,它纯粹是Scala如何支持currying的神器(为了完整性,请参阅下面的更详尽的解释,了解curry是什么,以及为什么我们都喜欢它).

形式上,函数可以具有零个或多个参数列表,每个参数列表具有零个或多个参数.
这意味着以下是有效的:def a,def b(),也是做作def c()()def d(x: Int)()()(y: Int)等...

函数def foo = ???具有零参数列表.函数def bar() = ???只有一个参数列表,参数为零.引入将这两种形式混为一谈的其他规则会破坏currying作为一致的语言特征:def a在形式上def b()def c()()两者之间都是等同的; def d(x: Int)()()(y: Int)相当于def e()(x: Int)(y: Int)()().

currying无关紧要的一个案例是处理Java互操作时.Java不支持currying,因此将零参数方法"test".length()(如直接调用java.lang.String#length())引入的语法糖也可以调用为没有问题"test".length.

关于currying的快速解释

Scala支持一种名为"currying"的语言功能,以数学家Haskell Curry的名字命名.
Currying允许您使用多个参数列表定义函数,例如:

def add(a: Int)(b: Int): Int = a + b
add(2)(3) // 5
Run Code Online (Sandbox Code Playgroud)

这很有用,因为您现在可以inc根据部分应用来定义add:

def inc: Int => Int = add(1)
inc(2) // 3
Run Code Online (Sandbox Code Playgroud)

Currying通常被视为通过库引入控制结构的一种方式,例如:

def repeat(n: Int)(thunk: => Any): Unit = (1 to n) foreach { _ => thunk }

repeat(2) {
  println("Hello, world")
}

// Hello, world
// Hello, world
Run Code Online (Sandbox Code Playgroud)

作为回顾,看看如何repeat开辟另一个使用currying的机会:

def twice: (=> Any) => Unit = repeat(2)

twice {
  println("Hello, world")
}

// ... you get the picture :-)
Run Code Online (Sandbox Code Playgroud)


som*_*ytt 14

关于ML周期性问题的一个好处是有定期答案.

谁能抵抗一个名为"我们有什么问题?"的帖子.

https://groups.google.com/forum/#!topic/scala-debate/h2Rej7LlB2A

来自:martin odersky日期:2012年3月2日星期五下午12:13主题:回复:[scala-debate]我们有什么问题...

有些人认为"我们错了"是因为我们正在努力向后弯曲以使Java成语在Scala中顺利运行.原则上可以说def length()和def length是不同的,抱歉,String是一个Java类,所以你必须编写s.length(),而不是s.length.我们很难通过承认从s.length到s.length()的自动转换来完成它.这是有问题的.推广这样,以便在类型系统中识别出两者,这将是一种可靠的方法.你怎么消除歧义:

type Action =()=>()def foo:Action

那么foo是Action还是()?那个foo()呢?

马丁

我最喜欢这个帖子的paulp小说:

On Fri, Mar 2, 2012 at 10:15 AM, Rex Kerr <ich...@gmail.com> wrote:

>This would leave you unable to distinguish between the two with 
>structural types, but how often is the case when you desperately 
>want to distinguish the two compared to the case where distinguishing 
>between the two is a hassle?


/** Note to maintenance programmer: It is important that this method be
 *  callable by classes which have a 'def foo(): Int' but not by classes which
 *  merely have a 'def foo: Int'.  The correctness of this application depends
 *  on maintaining this distinction.
 *  
 *  Additional note to maintenance programmer: I have moved to zambia.
 *  There is no forwarding address.  You will never find me.
 */
def actOnFoo(...)
Run Code Online (Sandbox Code Playgroud)

因此,该功能的潜在动机是生成这种ML线程.

另外一点googlology:

2010年4月1日星期四下午8:04,Rex Kerr <[hidden email]>写道:2010年4月1日星期四下午1点,richard emberson <[hidden email]>写道:

我假设"def getName:String"与"def getName():String"相同

不,实际上,他们不是.即使它们都调用没有参数的方法,一个是"具有零参数列表的方法",而另一个是"具有一个空参数列表的方法".如果你想更加困惑,请尝试def getName()():String(并创建一个带有该签名的类)!

Scala将参数表示为列表列表,而不仅仅是列表,以及

List()!= List(List())

这是一种奇怪的烦恼,特别是因为两者之间的差别很小,因为两者都可以自动转换为函数signature()=> String.

真正.实际上,无参数方法和具有空参数列表的方法之间的任何混合完全归因于Java互操作.它们应该是不同的,但是然后处理Java方法会太痛苦.你能想象每次读取字符串的长度时都要编写str.length()吗?

干杯


Dan*_*ral 10

首先,() => X=> X无参数方法完全无关.

现在,写这样的东西看起来很傻:

var x() = 5
val y() = 2
x() = x() + y()
Run Code Online (Sandbox Code Playgroud)

现在,如果您不遵循上述与无参数方法有关的内容,那么您应该查找统一访问原则.以上所有都是方法声明,所有这些都可以替换为def.也就是说,假设您删除了它们的括号.

  • 这看起来很愚蠢,但是它比任何更简单的`val b = 5`都可以覆盖`def b():Int`,而后者但不是前者将匹配`B <:{def b():Int除非你把子类强制转换回超类?LSP似乎在这里被淘汰了. (2认同)

0__*_*0__ 8

除了提到的惯例事实(副作用与非副作用)之外,它还有助于以下几种情况:

空父的用处

// short apply syntax

object A {
  def apply() = 33
}

object B {
  def apply   = 33
}

A()   // works
B()   // does not work

// using in place of a curried function

object C {
  def m()() = ()
}

val f: () => () => Unit = C.m
Run Code Online (Sandbox Code Playgroud)

没有父母的用处

// val <=> def, var <=> two related defs

trait T { def a:   Int; def a_=(v: Int): Unit }
trait U { def a(): Int; def a_=(v: Int): Unit }

def tt(t: T): Unit = t.a += 1  // works
def tu(u: U): Unit = u.a += 1  // does not work

// avoiding clutter with apply the other way round

object D {
  def a   = Vector(1, 2, 3)
  def b() = Vector(1, 2, 3)
}

D.a(0)  // works
D.b(0)  // does not work

// object can stand for no-paren method

trait E
trait F { def f:   E }
trait G { def f(): E }

object H extends F {
  object f extends E  // works
}

object I extends G {
  object f extends E  // does not work
}
Run Code Online (Sandbox Code Playgroud)

因此,就语言的规律性而言,区分是有意义的(特别是对于最后显示的情况)。


kir*_*uku 4

我想说两者都是可能的,因为您可以使用无参数方法访问可变状态:

class X(private var x: Int) {
  def inc() { x += 1 }
  def value = x
}
Run Code Online (Sandbox Code Playgroud)

该方法value没有副作用(它只访问可变状态)。在《Scala 编程》中明确提到了这种行为:

这种无参数方法在 Scala 中很常见。相比之下,使用空括号定义的方法(例如 def height(): Int)称为空括号方法。推荐的约定是,只要没有参数,就使用无参数方法,并且该方法仅通过读取包含对象的字段来访问可变状态(特别是,它不会更改可变状态)。

该约定支持统一访问原则 [...]

总而言之,Scala 中鼓励将不带参数且无副作用的方法定义为无参数方法,即省略空括号。另一方面,您永远不应该定义一个没有括号的有副作用的方法,因为这样该方法的调用看起来就像一个字段选择。

  • 这只是一个约定。编译器根本不帮助强制执行这一点,这使得它只比无用略好一些。(如果你指望它,那还不如没用。) (7认同)