Search code examples
scalasyntaxsemicolon-inference

When is semicolon mandatory in scala?


I am learning how to program in Scala and was being told that semicolon is optional in Scala. So with that in mind, I tried with the following nested block of code which does not have semi colon. However, it throws an error in the 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

And the sample with semi colon worked perfectly fine.

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

Therefore it seems to me that semi colon is not really optional and is mandatory in some situations. May I ask in what situation the semi colon is mandatory?


Solution

  • I'll try to extract the essence from your example.

    Consider the following code snippet:

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

    To the compiler, it looks like syntactic sugar for

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

    But the object 1 does not have an apply method that takes blocks, therefore the compiler produces an error:

    error: Int(1) does not take parameters

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

    Contrast this to

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

    This works, because I now does have an apply method.

    To make the differentiation between these two cases a little bit easier, the compiler requires a semicolon in the first case.

    Now one might wonder why a line break between val x = 1 and the { is not converted into an inferred semicolon. I think the relevant quote from the spec would be this (1.2 Newline Characters) (most parts of enumerations omitted ([...]), emphasis mine):

    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,

      [...]

    Note that this quote covers only the case with a single optional line break. It does not hold for two or more consecutive line breaks, e.g.

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

    is valid, and { val y = 2 } is parsed as a separate expression.

    I guess the motivation was to allow embedded DSL's with syntactic sugar like this:

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

    It would be really strange if one had to enclose each such MY_WHILE-statement into an additional pair of round parentheses, wouldn't it?