3

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?

Mox
  • 2,355
  • 2
  • 26
  • 42
  • @Bergi, I doubt so https://softwareengineering.stackexchange.com/questions/204278/should-i-use-semicolons-to-delimit-scala-statements – Mox Aug 25 '18 at 13:23
  • 1
    @Bergi What are you talking about? Here is [a typical piece of Scala code](https://github.com/scala/scala/blob/2.13.x/src/library/scala/collection/mutable/HashMap.scala). It contains three semicolons per 177 lines, exactly in the situation where multiple expressions are wrapped in `{}` (for no good reason, in this case, all those semicolons could be omitted, but the author was too lazy to type a line break, apparently). – Andrey Tyukin Aug 25 '18 at 13:25
  • @AndreyTyukin, you mean the result of my error is due to missing line break? – Mox Aug 25 '18 at 13:26
  • 1
    Most of the time (I'd estimate around 99.5% or so), the semicolon is mandatory only when the expression without the semicolon doesn't make any sense. For example, `{val b = a * 2 { val c = b + 4 ; c }}` does not make any sense, because `2` does not have a method `apply` that could take the block `{ val c = b + 4; c }` as argument. Contrast it to e.g. `Future` companion object, where you can write `Future { val c = b + 4; c }` without problems, because unlike `2`, the object `Future` *does provide* an appropriate `apply` method. – Andrey Tyukin Aug 25 '18 at 13:28
  • @AndreyTyukin, Thanks for the explanation. I thought that putting the statements on multiple lines will be suffice in this case. – Mox Aug 25 '18 at 13:31

2 Answers2

7

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?

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
5

Adding to Andrey's answer, it's rare that you write code like this in idiomatic Scala, but, when you do, you should use locally:

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

This case is exactly why locally exists.

Brian McCutchon
  • 8,354
  • 3
  • 33
  • 45