Scala 3:在赋值前使用变量时意外成功编译

bzzcjhmw  于 5个月前  发布在  Scala
关注(0)|答案(1)|浏览(48)

我希望这会引发一个错误,因为我试图在赋值之前使用x。下面是Scala 3.3.1 REPL:

scala> val x: Int =
     |  val y: Int = x + 10
     |  y
     |
val x: Int = 10

字符串
为什么结果是10?当Scala计算x + 10时,它假设x=0

cld4siwp

cld4siwp1#

这是因为相同的REPL技巧,允许您编译和执行

> val x = 1
> val x = 2

字符串
在交互式REPL模式中:val s不是作为函数体内部的局部变量编译的,而是作为 Package 器对象的成员,如described here
在您的特定情况下,当您键入示例时,

scala> val x: Int =
     |  val y: Int = x + 10
     |  y
     |


在REPL中,它被 Package 成一个合成的“execution template“对象,其行为大致如下:

object replInputLine$1 {
  val x: Int =
    val y = x + 10
    y
  println(x)
}

@main def entry(): Unit =
  replInputLine$1


在这个脚本中发生了什么?好吧,replInputLine$1-对象被初始化了。像在对象初始化过程中一样,在对象分配过程中,成员x: Int被分配并初始化为0,然后执行实际的对象初始化器,它将x更新为10
由于x是一个对象成员,而不是方法体中的局部变量,因此可以毫无问题地访问它。
相反,如果你试图在方法体中使用相同的代码片段,你会得到编译错误,如下面的例子所示:

@main def entry(): Unit =
  val x: Int =
    val y: Int = x + 10
    y
  println(x)


这将导致编译失败,并显示以下消息
引用错误:[...] x是扩展到x定义的前向引用
因此,它只是一个工件,因为REPL将val s视为合成对象的成员,而不是将它们视为局部变量。
用REPL玩“语言律师”游戏是毫无意义和无聊的:如果你想“真实的”理解编译器在做什么,不要在REPL中测试它,编写一个单独的程序,然后编译它。

相关问题