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

时间:2012-09-08 22:18:59

标签: scala

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

我能想到的缺点:

  • 令人困惑。每周或每两周都会有一些问题或Scala邮件列表中的问题。
  • 这很复杂;我们还必须区分() => X=> X
  • 这是不明确的:x.toFoo(y)是什么意思,或x.toFoo.apply(y)? (答案:这取决于x toFoo方法的重载以及Foo的{​​{1}}方法的重载,但如果发生冲突,则不会在您尝试调用之前看到错误。)
  • 它混淆了运算符样式方法调用语法:链接方法时没有用于代替参数的符号,或者最后避免使用分号干扰。使用零参数方法,您可以使用空参数列表apply

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

为什么不让方法Function0def foo完全相同,并允许在括号或不带括号的情况下调用它们?它的优点是什么?

5 个答案:

答案 0 :(得分:27)

Currying,这就是为什么

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

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

正式地,函数可以具有零个或多个参数列表,每个参数列表具有零个或多个参数 这意味着以下内容有效:def adef 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

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

def inc: Int => Int = add(1)
inc(2) // 3

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

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

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

// Hello, world
// Hello, world

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

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

twice {
  println("Hello, world")
}

// ... you get the picture :-)

答案 1 :(得分:14)

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

谁能抵抗一个名为“我们有什么问题?”的帖子。

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

  

来自:martin odersky日期:2012年3月2日星期五   12:13 PM主题:回复:[斯卡拉辩论]我们有什么问题......

     

有些人认为“我们错了”是我们正在努力弯曲   过度使Java成语在Scala中顺利运行。该   原则上可以说def length()和def length   是不同的,抱歉,String是一个Java类,所以你必须写   s.length(),而不是s.length。我们的工作非常努力   允许从s.length到s.length()的自动转换。那是   虽然有问题。概括,以便确定两者   在类型系统中将是一个确定的厄运方式。你怎么样?   消歧:

     

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

     

那么foo是Action还是()?怎么样foo()?

     

马丁

我最喜欢的那个秘密小说:

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(...)

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

另外一点googlology:

  

2010年4月1日星期四下午8:04,Rex Kerr&lt; [隐藏电子邮件]&gt;写道:开   2010年4月1日星期四下午1点,richard emberson&lt; [隐藏电子邮件]&gt;写道:

     
    
      

我假设“def getName:String”与“def getName():String”

相同     
         

不,实际上,他们不是。尽管他们都称之为方法     没有参数,一个是“零参数列表的方法”而     另一个是“带有一个空参数列表的方法”。如果你想     更加困惑,尝试def getName()():String(并创建一个     带有那个签名的课程)!

         

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

         

List()!= List(List())

         

这是一种奇怪的烦恼,特别是因为这么少     否则两者之间的区别,因为两者都可以     自动变成函数签名()=&gt;字符串。

  
     

真。事实上,无参数方法与之间的任何混淆   具有空参数列表的方法完全归功于Java互操作。   它们应该是不同的,但随后处理Java方法   太痛苦了。你能想象每个都要编写str.length()吗?   你花时间长度的字符串?

     

干杯

答案 2 :(得分:10)

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

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

var x() = 5
val y() = 2
x() = x() + y()

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

答案 3 :(得分:4)

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

class X(private var x: Int) {
  def inc() { x += 1 }
  def value = x
}

方法value没有副作用(它只访问可变状态)。 Programming in Scala中明确提到了此行为:

  

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

     

该惯例支持统一访问原则[...]

     

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

答案 4 :(得分:4)

除了提到的常规事实(副作用与非副作用)之外,它还有几种情况:

拥有空洞的实用性

// 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

没有帮助的有用性

// 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
}

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