Nested Dangers
Although you should avoid nesting if
s, you are sometimes forced to do so in order to get things out the door with a major rewrite, which may cause more problems to accidentally be created instead of solved.
Consider this perfectly acceptable chunk of code:
val (thing1, thing2) = {
if (cond1) {
7, 8
} else {
3, 4
}
}
Suppose we need to add an extra branch to check for a new condition:
val (thing1, thing2) = {
if (cond1) {
if (cond2) {
(1, 2)
}
val something = doSomething()
if (something) {
(5, 6)
}
(7, 8)
} else {
(3, 4)
}
}
This change would seem intuitive to the early Scala programmer because you get hooked on ditching the return
keyword fast.
This change looks inconspicuous, but there’s a huge issue that’s easy to miss!
Consider the behavior on the following set of inputs:
val cond1 = true
val cond2 = true
def doSomething(): Boolean = true
What are the values of thing1
and thing2
?
You may be shocked to realize after a first pass that they were (7, 8)
?
What happened here?
Well, it’s very easy to do!
This is a great example of what happens when we combine functional and object-oriented principles.
Let’s look at this block:
if (cond2) {
(1, 2)
}
This block returns (1, 2)
, but the result is not stored into anything!
If we write a return
, we will anger the Scala compiler, which does not want us to ever call return
and will give us a compiler warning!
If we had simply used the return
keyword, we would get what we intended: (1, 2)
.
Getting rid of syntax isn’t always the answer! Everything is always a balance!