Saturday, March 28, 2009

Nullable AnyVal in Scala

DISCLAIMER: Please don't treat it too seriously. And never ever use the following code for anything serious.

Usually we want to get rid of potential nulls in the code. Here I'll go the opposite way...

All things in Scala are instances of classes. Full OO. The base type for everything is Any. On the other hand there are simple types (still objects!) that inherit from AnyVal and complex types that inherit from AnyRef.

Classes in AnyVal family represent primitive types from underlying (JVM) platform. The compiler makes the decision when to do (un)boxing, so that everything works optimally and (what's even more important) according Scala semantics.

The very important fact is that in Scala null has type Null. That special class is a subclass of every AnyRef derived class. In practice it means that every variable of type Any, AnyRef or AnyRef's subtypes may have a value null. We say that they are nullable.

This is a cause of many bugs... many programming languages aficionados wish that nulls never existed.

Back to the topic. Variables of type AnyVal (or subtypes) normally can not be nulled.

val anyval1: AnyVal = null
/* error: type mismatch;
found : Null(null)
required: AnyVal

But there is a unique Scala functionality (implicit defs aka. views) and a bug in the compiler that allows for that.

implicit def theOtherNull(n: Null): AnyVal = { object o { var nn: AnyVal = _ }; o.nn }

After defining this view the following code will magically work.

val anyval2: AnyVal = null

println(x == null) // "true" !

Uninitialized variable in classes have to have a value. Even if they are AnyVals there needs to be a special default/uninitialized value. Unfortunately as of now that value is a null from underlying JVM platform. Of course it's a bug, becouse it breaks the type system... even worse thing is that this weird null value can be propagated to other vals/vars of type AnyVal.
In case of the view above it's returned, the type checking part of the compiler assumes that the returned value will be a healthy AnyVal, but it's not so anyval2 get's that forbidden/improperly typed value.

1 comment:

  1. More sadness:

    scala> class X[T <: AnyVal]{ def y: AnyVal = null.asInstanceOf[T] }
    defined class X

    scala> (new X).y
    res0: AnyVal = null