=>,()=>和Unit =>之间的区别是什么

Mal*_*lio 147 scala

我试图表示一个不带参数的函数并且不返回任何值(我在JavaScript中模拟setTimeout函数,如果你必须知道的话.)

case class Scheduled(time : Int, callback :  => Unit)
Run Code Online (Sandbox Code Playgroud)

不编译,说"`val'参数可能不是按名称调用"

case class Scheduled(time : Int, callback :  () => Unit)  
Run Code Online (Sandbox Code Playgroud)

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

Scheduled(40, { println("x") } )
Run Code Online (Sandbox Code Playgroud)

我必须这样做

Scheduled(40, { () => println("x") } )      
Run Code Online (Sandbox Code Playgroud)

什么也有效

class Scheduled(time : Int, callback :  Unit => Unit)
Run Code Online (Sandbox Code Playgroud)

但是以一种更加明智的方式被引用

 Scheduled(40, { x : Unit => println("x") } )
Run Code Online (Sandbox Code Playgroud)

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

 Scheduled(40, println("x") )
Run Code Online (Sandbox Code Playgroud)

给宝宝他的瓶子!

Dan*_*ral 227

按名称呼叫:=>类型

=> Type符号代表的call-by-的名字,这是一个很多方面的参数都可以通过.如果你不熟悉它们,我建议花一些时间来阅读维基百科的文章,尽管现在它主要是按值调用和按引用调用.

这意味着传递的内容将替换函数内的值名称.例如,使用此功能:

def f(x: => Int) = x * x
Run Code Online (Sandbox Code Playgroud)

如果我这样称呼它

var y = 0
f { y += 1; y }
Run Code Online (Sandbox Code Playgroud)

然后代码将像这样执行

{ y += 1; y } * { y += 1; y }
Run Code Online (Sandbox Code Playgroud)

虽然这提出了如果存在标识符名称冲突会发生什么的点.在传统的按名称调用中,会发生一种称为捕获避免替换的机制,以避免名称冲突.但是,在Scala中,这是以另一种方式实现的,结果相同 - 参数内的标识符名称不能引用被调用函数中的或标识符.

在解释其他两个问题之后,我还会谈到其他一些与名字相关的要点.

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

语法() => Type代表a的类型Function0.也就是说,一个不带参数并返回一些东西的函数.这相当于调用方法size()- 它不需要参数并返回一个数字.

然而,有趣的是,这种语法与匿名函数文字的语法非常相似,这是造成一些混淆的原因.例如,

() => println("I'm an anonymous function")
Run Code Online (Sandbox Code Playgroud)

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

() => Unit
Run Code Online (Sandbox Code Playgroud)

所以我们可以写:

val f: () => Unit = () => println("I'm an anonymous function")
Run Code Online (Sandbox Code Playgroud)

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

单位=>类型

这实际上只是一个Function1,其第一个参数是类型Unit.写它的其他方法是(Unit) => TypeFunction1[Unit, Type].事情是......这不太可能是人们想要的.该Unit类型的主要目的是指示一个人不感兴趣的值,因此接收该值没有意义.

例如,考虑一下

def f(x: Unit) = ...
Run Code Online (Sandbox Code Playgroud)

有什么可以做的x?它只能有一个值,因此无需接收它.一种可能的用途是链接函数返回Unit:

val f = (x: Unit) => println("I'm f")
val g = (x: Unit) => println("I'm g")
val h = f andThen g
Run Code Online (Sandbox Code Playgroud)

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

混乱的根源

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

() => { println("Hi!") }
Run Code Online (Sandbox Code Playgroud)

是文字为() => Unit,然后

{ println("Hi!") }
Run Code Online (Sandbox Code Playgroud)

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

混淆的另一个原因是写入了Unit类型的(),它看起来像一个0-arity参数列表(但它不是).

  • @Alex 这与 `(Unit) => Type` 与 `() => Type` 的区别相同 - 第一个是 `Function1[Unit, Type]`,而第二个是 `Function0[Type]`。 (3认同)
  • @ nish1013对于某些对象,“ literal”是一个值(整数“ 1”,字符“ a”,字符串“ abc”或函数(()=> println(“ here”)))例子)。它可以作为参数传递,也可以存储在变量中,等等。“代码块”是语句的句法定界-它不是值,不能传递,或类似的东西。 (2认同)

Ben*_*son 36

case class Scheduled(time : Int, callback :  => Unit)
Run Code Online (Sandbox Code Playgroud)

case修改使得隐含val了每个参数的构造函数.因此(如有人指出)如果删除case,可以使用call-by-name参数.无论如何编译器可能允许它,但如果它创建val callback而不是变形,它可能会让人感到惊讶lazy val callback.

当你改为callback: () => Unit现在你的情况只需要一个函数而不是一个call-by-name参数.显然这个功能可以存储,val callback所以没有问题.

获得所需内容的最简单方法(Scheduled(40, println("x") )使用call-by-name参数传递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 })
}
Run Code Online (Sandbox Code Playgroud)

正在使用:

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

scala> Scheduled(1234, println("x")).doit
x
Run Code Online (Sandbox Code Playgroud)

  • 为什么不把它保留为case-class并且只是覆盖默认的apply?此外,编译器不能将名称转换为延迟val,因为它们具有固有的不同语义,懒惰是最多一次而且按名称具有每个引用 (3认同)