什么时候分号在scala中是强制性的?

When is semicolon mandatory in scala?

我正在学习如何在 Scala 中编程,并被告知分号在 Scala 中是可选的。因此,考虑到这一点,我尝试使用以下没有分号的嵌套代码块。但是,它会在 Scala REPL

中引发错误
scala> { val a = 1
 | {val b = a * 2
 | {val c = b + 4
 | c}
 | }
 | }
<console>:17: error: Int(1) does not take parameters
   {val b = a * 2

带分号的样本工作得很好。

scala> { val a = 1;
 | { val b = a*2;
 | { val c = b+4; c}
 | }
 | }
res22: Int = 6

因此在我看来,分号并不是真正可选的,在某些情况下是强制性的。请问分号在什么情况下是必须的?

我将尝试从您的示例中提取精华。

考虑以下代码片段:

{ val x = 1 { val y = 2 } }

对于编译器来说,它看起来像是

的语法糖
{ val x = 1.apply({ val y = 2 }) }

但是对象 1 没有 apply 接受块的方法,因此编译器产生错误:

error: Int(1) does not take parameters

  { val x = 1 { val y = 2 } }
              ^

对比

object I { def apply(a: => Any): Unit = () }
{ val x = I { val y = 2 } }

这行得通,因为 I 现在 有一个 apply 方法。

为了更容易区分这两种情况,编译器要求在第一种情况下使用分号。

现在有人可能想知道为什么 val x = 1{ 之间的换行符没有转换为推断的分号。我认为规范中的相关引用是这样的(1.2 Newline Characters)(省略了大部分枚举([...]),重点是我的):

The Scala grammar [...] contains productions where optional nl tokens, but not semicolons, are accepted. This has the effect that a newline in one of these positions does not terminate an expression or statement. These positions can be summarized as follows:

[...]

  • in front of an opening brace ‘{’, if that brace is a legal continuation of the current statement or expression,

    [...]

请注意,此引用仅涵盖带有 单个 可选换行符的情况。它不适用于两个或多个连续的换行符,例如

scala> {
     |   val x = 1
     | 
     |   { val y = 2 }
     | }

有效,{ val y = 2 }被解析为单独的表达式。

猜测 其动机是允许嵌入式 DSL 具有如下语法糖:

MY_WHILE(x >= 0)
{
  println(x)
  x -= 1
}

如果必须将每个这样的 MY_WHILE 语句括在一对额外的圆括号中,那真的很奇怪,不是吗?

添加到 Andrey 的回答中,您很少在惯用的 Scala 中编写这样的代码,但是,当您这样做时,您应该使用 locally:

{
  val a = 1
  locally {
    val b = a * 2
    locally {
      val c = b + 4
      c
    }
  }
}

这种情况正是 locally 存在的原因。