=>,()=>和Unit =>有什么区别


153

我试图表示一个不带参数也不返回任何值的函数(如果您一定要知道的话,我正在用JavaScript模拟setTimeout函数。)

case class Scheduled(time : Int, callback :  => Unit)

无法编译,说““ val”参数可能不是按名称调用”

case class Scheduled(time : Int, callback :  () => Unit)  

编译,但是必须被奇怪地调用,而不是

Scheduled(40, { println("x") } )

我必须这样做

Scheduled(40, { () => println("x") } )      

也有效的是

class Scheduled(time : Int, callback :  Unit => Unit)

但以一种不太明智的方式被调用

 Scheduled(40, { x : Unit => println("x") } )

(一个类型为Unit的变量是什么?)当然,我想要的是一个构造函数,如果它是一个普通函数,则可以用我调用它的方式来调用它:

 Scheduled(40, println("x") )

给婴儿他的瓶子!


3
将案例类与按名称命名的parms一起使用的另一种方法是将它们放在辅助参数列表中,例如case class Scheduled(time: Int)(callback: => Unit)。这是可行的,因为辅助参数列表未公开公开,也未包含在生成的equals/ hashCode方法中。
nilskp,2015年

有关此问题和答案的一些更有趣的方面涉及到别名参数和0 arity函数之间的差异。当我发现这个问题时,它实际上就是我要寻找的东西。
lex82 '16

Answers:


234

姓名呼叫:=>类型

=> Type符号代表按名称调用,这是可以传递参数的多种方式之一。如果您不熟悉它们,我建议您花一些时间阅读该Wikipedia文章,即使现在它主要是按值调用和按引用调用。

这意味着将传递的内容替换为函数内部的值名称。例如,使用以下功能:

def f(x: => Int) = x * x

如果我这样称呼它

var y = 0
f { y += 1; y }

然后代码将像这样执行

{ y += 1; y } * { y += 1; y }

虽然这提出了标识符名称冲突的情况。在传统的按名称呼叫中,发生了一种称为捕获避免替换的机制来避免名称冲突。但是,在Scala中,这是通过另一种方式实现的,结果是相同的-参数内的标识符名称无法引用,或者被调用函数中的影子标识符。

在解释其他两点之后,我还会谈到与按名字呼叫有关的其他几点。

0-arity函数:()=>类型

语法() => Type代表的类型Function0。也就是说,一个不带任何参数并返回某些内容的函数。这就相当于调用该方法size()-它不带任何参数并返回一个数字。

但是,有趣的是,此语法与匿名函数文字的语法非常相似,这是造成混淆的原因。例如,

() => println("I'm an anonymous function")

是值为0的匿名函数文字,其类型

() => Unit

所以我们可以这样写:

val f: () => Unit = () => println("I'm an anonymous function")

但是,重要的是不要将类型与值混淆。

单位=>类型

这实际上只是一个Function1,其第一个参数是type Unit。编写它的其他方式是(Unit) => TypeFunction1[Unit, Type]。问题是……这不可能永远是人们想要的。该Unit类型的主要目的是指示一个不感兴趣的值,因此接收该值没有任何意义。

例如,考虑

def f(x: Unit) = ...

一个人可能怎么办x?它只能有一个值,因此不需要接收它。一种可能的用途是链接函数返回Unit

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g

因为andThen仅在上定义Function1,并且要链接的函数返回Unit,所以我们必须将它们定义为Function1[Unit, Unit]能够链接它们的类型。

混乱的根源

造成混淆的第一个原因是认为0 arity函数存在的类型和文字之间的相似性也存在于按名称调用中。换句话说,因为

() => { println("Hi!") }

是的字面量() => Unit,然后

{ println("Hi!") }

将是一个字面意思=> Unit。它不是。那是一段代码,而不是文字。

另一个造成混淆的原因是Unit类型的被写入(),看起来像一个0参量的参数列表(但事实并非如此)。


两年后,我可能必须成为第一个拒绝投票的人。有人想知道在圣诞节是否使用case =>语法,我不能推荐这个答案,因为它规范而完整!世界将走向何方?也许玛雅人刚离开一个星期。他们正确地计算出leap年吗?夏令时?
som-snytt

@ som-snytt嗯,这个问题并没有提出case ... =>,所以我没有提及。悲伤但真实。:-)
Daniel C. Sobral

1
@Daniel C. Sobral您能否解释一下“那是代码块,而不是文字。” 部分。那么两者之间的确切区别是什么?
nish1013 2013年

2
@ nish1013“文字”是一个值(对于某些示例,是整数1,字符'a',字符串"abc"或函数() => println("here"))。它可以作为参数传递,也可以存储在变量中,等等。“代码块”是语句的句法分隔-它不是值,不能传递,或类似的东西。
Daniel C. Sobral

1
@Alex (Unit) => Type与vs的区别() => Type-第一个是a Function1[Unit, Type],第二个是a Function0[Type]
Daniel C. Sobral 2014年

36
case class Scheduled(time : Int, callback :  => Unit)

case修改使隐性val每个参数的构造出来。因此(如某人所述),如果您将其删除case,则可以使用“按姓名致电”参数。编译器可能仍然允许它,但是如果创建val callback而不是将其变形为,则可能会使人感到惊讶lazy val callback

当您更改为callback: () => Unit现在时,您的案例仅采用一个函数而不是按名字调用参数。显然,该函数可以存储在其中,val callback所以没有问题。

获得所需内容的最简单方法(Scheduled(40, println("x") )使用“按名字调用”参数传递lambda)可能是跳过case并显式创建apply您最初无法获得的:

class Scheduled(val time: Int, val callback: () => Unit) {
    def doit = callback()
}

object Scheduled {
    def apply(time: Int, callback: => Unit) =
        new Scheduled(time, { () => callback })
}

正在使用:

scala> Scheduled(1234, println("x"))
res0: Scheduled = Scheduled@5eb10190

scala> Scheduled(1234, println("x")).doit
x

3
为什么不将其保留为案例类,而仅覆盖默认应用?另外,编译器无法将别名转换为惰性val,因为它们的内在语义不同,惰性是最多一次,并且别名具有每个引用
Viktor Klang 2010年

@ViktorKlang如何覆盖案例类的默认apply方法?stackoverflow.com/questions/2660975/…–
Sawyer

对象ClassName {def apply(…):…=…}
Viktor Klang

四年后,我意识到我选择的答案仅回答了标题中的问题,而不是我实际遇到的问题(此问题确实回答了这个问题)。
马尔沃里奥2015年

1

在问题中,您想在JavaScript中模拟SetTimeOut函数。基于先前的答案,我编写以下代码:

class Scheduled(time: Int, cb: => Unit) {
  private def runCb = cb
}

object Scheduled {
  def apply(time: Int, cb: => Unit) = {
    val instance = new Scheduled(time, cb)
    Thread.sleep(time*1000)
    instance.runCb
  }
}

在REPL中,我们可以得到以下内容:

scala> Scheduled(10, println("a")); Scheduled(1, println("b"))
a
b

我们的模拟与SetTimeOut的行为并不完全相同,因为我们的模拟是阻止函数,但SetTimeOut是非阻止的。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.