What’s Nothing, my precious?
Nothing is a kind of special type in Scala’s class hierarchy.
To begin with, it is not instantiable: we cannot generate an instance from it by any means.
Weird, isn’t it? This is understood a bit better with the second peculiarity: it is a type that extends aaaaall classes. Even those that are user defined? Even those.
Then, what’s the use in it? The better example is None. If Option[T] weren’t covariant, we would need to define a None[T] for every T. And then, None[Int] would be different from em>None[String]… you can imagine what a mess would that be!
But since Option[+T] is covariant*, and Nothing a subtype of every class, we needn’t declare a None for every T in the list, but there’s only one None with the following signature:
object None extends Option[Nothing] //...with Product with Serializable
Same thing applies to Nil.
When we define methods or values in Scala, at first and being quite purist, we define their type:
val myVal: String = "hi" def myMethod(n: String): Boolean = (n % 2 == 0)
But it is quite likely that, as time goes by and after a lot of lines of code, we start forgetting to add the type that these expressions return:
val myVal = "hi" def myMethod(n: String) = (n % 2 == 0)
Nothing happens, compiler is capable of inferring the returned types… as long as there are enough hints to figure them out. It’s in this case when the “Nothing” bogeyman menace arises.
Imagine we have a generic method like this:
def getFirstElement[T](list: List[T]): Option[T] = list.headOption
What this method does is to return the first element in a list (if there’s some). Where can all start to go wrong?
Suppose we want to test our method on an empty list. Our experience tells us that the best way to do that is by using List’s apply method with no argument: List().
However, if we pass this as a parameter to our method:
val first = getFirstElement(List())
Let’s see what is returned:
first: Option[Nothing] = None
We shouldn’t be mean to the compiler, let’s face that we have given no facility for it to infer the correct type. We could do it simply by using the empty method of the List object:
val first = getFirstElement(List.empty[Int]) first: Option[Int] = None
though the type could also be inferred by adding the parameter type in the method call:
val first = getFirstElement[Int](List()) first: Option[Int] = None
Done! Now I think about it, this post looks a bit dull without any memes in it…
* Covariance: C[T] is said to be covariant in T, if given a certainV<:T, then C[V]<:C[T] . This issue will be treated in detail in future (but not that remote) posts 🙂