Let's focus on the subject of this post: how you can prevent null pointer exceptions with Scala. Suppose you have your tidy Scala code in functional flavor with all the fixes, and you need to do a series of calls to a Java library, where each value depends on the previous value: let's say you want to call functions A,B,and C.
val x=A(in)
val y=B(x)
val z=C(y)
(or C(B(A(in))) if you like )
Now, is common in Java to return null when no value can be returned or even throw an exception if something goes wrong. How we prevent that? The naive approach will be to chain the ifs:
val x=A(in)
if (x != null){
val y=B(x)
if (x != null){
val z=C(y)
}
}
I don't know you, but for me that style doesn't cut it anymore, is too verbose! And now our flow is littered with null checks, obscuring the purpose.
So, what we can do? I know! Let's use Scala's Option and write a small function that returns "Some" value or "None" if we got null or something wrong happened:
Nothing fancy here, Option(x) will evaluate x and return Some(x) if it has a value or None if is null. The only noteworthy thing here is in the signature: it defined as " x:=> T " that roughly means "any function that returns something of type T", so x will be evaluated when we actually use it and not when we call the function, otherwise we risk getting the exception before entering the try block.
Ok, so now we wrap every function call with our unsafeCall function, but now we have a problem, the types don't match! We can't just chain the calls, we need to extract the value from the Option first!
We can try with pattern matching:
A(x) match {t
case None => _
case Some(y) => B(y) match {
case None => _
case Some(z) => C(z)
}
}
But it looks clumsy and we don't gain much compared to the "if not null" version, what we can do?
Let's take a step back and look at the types: we want to chain a series of function with T=>Option[T] ... does it ring a bell? What if we write as T=>M[T] ? If you've said the M-word, bingo!
Some[_] is a monad, and that means we can use Scala's "for" notation.
Let's tie everything together in one example:
ADENDUM:
This is a toy example for illustrative purposes. I guess I should have left out the exceptions, otherwise is almost the same result as surrounding everything with a try/catch. Also, is an exception happens, we want to know what happens, for that Either is more suitable than Option (I'll leave that for another blog post).
Still, when you need to chain a series of functions that return Some/None, for comprehensions gives you a nice sugar ;)
Let's take a step back and look at the types: we want to chain a series of function with T=>Option[T] ... does it ring a bell? What if we write as T=>M[T] ? If you've said the M-word, bingo!
Some[_] is a monad, and that means we can use Scala's "for" notation.
Let's tie everything together in one example: