2

我正在学习如何在 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

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

4

2 回答 2

6

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

考虑以下代码片段:

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

对编译器来说,它看起来像语法糖

{ val x = 1.apply({ val y = 2 }) }

但是该对象1没有apply采用块的方法,因此编译器会产生错误:

错误:Int(1) 不带参数

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

对比一下

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

这行得通,因为Inow确实有一个apply方法。

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

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

Scala 语法 [...] 包含接受可选nl标记但不接受分号的产生式。这具有在这些位置之一中的换行符不会终止表达式或语句的效果。这些立场可以概括如下:

[...]

  • 在左大括号“{”之前,如果该大括号是当前语句或表达式的合法延续

    [...]

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

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

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

动机是允许嵌入式 DSL 使用这样的语法糖:

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

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

于 2018-08-25T13:48:06.727 回答
4

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

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

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

于 2018-08-25T16:35:35.980 回答