分号何时是强制性的?

时间:2018-08-25 13:17:10

标签: scala syntax semicolon-inference

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

因此,在我看来,半冒号并不是真正的可选,在某些情况下是必需的。请问在什么情况下必须使用半冒号?

2 个答案:

答案 0 :(得分:5)

我将尝试从您的示例中提取要点。

请考虑以下代码段:

{ 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 } }

之所以可行,是因为I现在确实有一种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语句放入另一对圆括号中,这真的很奇怪,不是吗?

答案 1 :(得分:3)

除了Andrey的答案外,很少有人会在惯用的Scala中编写像这样的代码,但是当您这样做时,应该使用locally

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

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