我希望这会引发一个错误,因为我试图在赋值之前使用x。下面是Scala 3.3.1 REPL:
x
scala> val x: Int = | val y: Int = x + 10 | y | val x: Int = 10
字符串为什么结果是10?当Scala计算x + 10时,它假设x=0?
x + 10
x=0
cld4siwp1#
这是因为相同的REPL技巧,允许您编译和执行
> val x = 1 > val x = 2
字符串在交互式REPL模式中:val s不是作为函数体内部的局部变量编译的,而是作为 Package 器对象的成员,如described here。在您的特定情况下,当您键入示例时,
val
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是一个对象成员,而不是方法体中的局部变量,因此可以毫无问题地访问它。相反,如果你试图在方法体中使用相同的代码片段,你会得到编译错误,如下面的例子所示:
replInputLine$1
x: Int
0
10
@main def entry(): Unit = val x: Int = val y: Int = x + 10 y println(x)
型这将导致编译失败,并显示以下消息引用错误:[...] x是扩展到x定义的前向引用因此,它只是一个工件,因为REPL将val s视为合成对象的成员,而不是将它们视为局部变量。用REPL玩“语言律师”游戏是毫无意义和无聊的:如果你想“真实的”理解编译器在做什么,不要在REPL中测试它,编写一个单独的程序,然后编译它。
1条答案
按热度按时间cld4siwp1#
这是因为相同的REPL技巧,允许您编译和执行
字符串
在交互式REPL模式中:
val
s不是作为函数体内部的局部变量编译的,而是作为 Package 器对象的成员,如described here。在您的特定情况下,当您键入示例时,
型
在REPL中,它被 Package 成一个合成的“execution template“对象,其行为大致如下:
型
在这个脚本中发生了什么?好吧,
replInputLine$1
-对象被初始化了。像在对象初始化过程中一样,在对象分配过程中,成员x: Int
被分配并初始化为0
,然后执行实际的对象初始化器,它将x
更新为10
。由于
x
是一个对象成员,而不是方法体中的局部变量,因此可以毫无问题地访问它。相反,如果你试图在方法体中使用相同的代码片段,你会得到编译错误,如下面的例子所示:
型
这将导致编译失败,并显示以下消息
引用错误:[...] x是扩展到x定义的前向引用
因此,它只是一个工件,因为REPL将
val
s视为合成对象的成员,而不是将它们视为局部变量。用REPL玩“语言律师”游戏是毫无意义和无聊的:如果你想“真实的”理解编译器在做什么,不要在REPL中测试它,编写一个单独的程序,然后编译它。