Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does whitespace matter in Scala?

Tags:

scala

When developing in Scala, I seem to frequently run into issues with white space having an unexpected impact on the meaning of the code.

Most recently, I'm having trouble with trying to write a multi-line boolean expression, where breaking the line seems to result in a compile error, i.e.

(oneVeryLongExpression < anotherVeryLongExpression) || ((a == b) && c);

...compiles just fine, but, if I'm tempted to split the line because of these very long expressions...

(oneVeryLongExpression < anotherVeryLongExpression) 
  || ((a == b) && c);

...it doesn't compile, which takes me by surprise.

I have found that I can work around that by adding an additional set of parentheses around the entire expression:

((oneVeryLongExpression < anotherVeryLongExpression) 
  || ((a == b) && c));

...but I'd still like to understand why Scala needs this hint to ignore the newline.

I also have issues when going the other direction: I occasionally convert a moderately large block of Scala code into a set of single-line code snippets, in order to enter each as a single line into a Scala-based shell that doesn't accept multi-line input.

Here's an example of a for-each that I needed to be able to run in my shell as a single line of input (for those who are curious about what I'm doing, I'm looking through a graph of my software for equality checks where the variable types on either side are of a particular type, to find places where x.equals(y) should have been used instead of x == y):

equalityChecks.foreach { node => {
  var lhsFound = false;
  var rhsFound = false;
  breakable {
    node.inEdges().foreach { edge => {
      if (nodesOfTypeT.contains(edge.originNode())) {
        if (edge.tagged(leftOperand)) {
          lhsFound = true;
        };
        if (e.taggedWith(rightOperand)) {
          rhsFound = true;
        };
        if (lhsFound && rhsFound) {
          doublyReachable.add(n);
          break; /* skip the rest of the edges and process next node */
        };
      }
    }}
  };
}};

Note the extra semicolons after the breakable block and the three if blocks, at least some of these were necessary in order to preserve the meaning of the code when the newlines are removed; without them, it does not compile.

Under what circumstances does a newline have semantic meaning in Scala? How can I tell, before I add or remove a newline, whether it will change the meaning of the code?

like image 445
Theodore Murdock Avatar asked Sep 05 '25 17:09

Theodore Murdock


1 Answers

(oneVeryLongExpression < anotherVeryLongExpression) is a proper expression in scala. When compiler traverses the code and meets this expression and nothing in the same line it reports it as the proper expression. Then the next line || ((a == b) && c); becomes unexpected and generates an error. You are right to use the parenthesis to make the compiler know about multiline expression in this case. You could also leave || in the first line to make the first line not valid expression:

(oneVeryLongExpression < anotherVeryLongExpression) ||
  ((a == b) && c);

Also scala REPL accepts multi-line inputs. Just enter :paste command and you can enter anything. Press ctrl-D to finish

As for the semicolons, they are rarely needed in scala. I think you ever need them either in for-comprehensions or if you put several separate expressions into one line. In your example node => ... composes of three expressions: var lhsFound = false, var rhsFound = false and breakable {...} Breakable forms a single big expression but it also is formed from a few if statements, so you need to separate each of them as they are separate expressions. This of course is applied only if you have everything as a single-liner. Still I don't think you should ever have to put those semicolons - just use paste mode in REPL.

I find myself using semicolons only in for-comprehensions or in short pattern matches (usually partial functions) where I want them to be a one-liner

P.S. Semicolon before } is always optional as it does not separate the expressions. I believe semicolons in your code in the if's after = true and break can be removed. All others are correct if you put all this code in one line

like image 123
Archeg Avatar answered Sep 09 '25 05:09

Archeg