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?
(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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With