So, unfortunately there’s at least one issue with all of the different existing approaches. The best one in terms of performance is the explicit null-checks, and the only problem with that is that it’s hard to read and write; but what if we could make it easy to do so?
Enter the macro
This is where the idea for the macro-based approach comes in. It allows us to create, what is essentially, a
source code ->
source code level transformation at compile time; or in layman’s terms, a code re-writing tool.
With this power, we can specify the property we’re trying to access, and have the macro rewrite it for us, as the fully explicitly null-checked version.
With this approach we get the best possible solution, something that is null-safe, easy to read and write, and efficient!
|Readable / Writable
|For loop flatMap
|Monocle Optional (lenses)
|thoughtworks NullSafe DSL
Key: ️ = Good, = Sub-optimal, = Bad
How to get it
To add it to your project, just add the dependency
and then import
and you’re good to go!
There’s also two other variants of the macro:
opt, which is useful for interoping with Java code and work as follows:
notNull which works like this:
All of the above work for method invocation as well as property access, and the two can be intermixed. For example:
will be translated properly.
Also the macro makes the arguments to method and function calls null-safe as well. So in the case of:
you don’t have to worry if
e would be
Custom default for
? macro, you can also provide a custom default instead of
null, by passing it in as the second parameter. For example
There’s also a
?? (null coalesce operator) which is used to select the first non-null value from a var-args list of expressions.
The null-safe coalesce operator also rewrites each arg so that it’s null safe. So you can pass in
a.b.c as an expression without worrying if
null. To be more explicit, the
?? macro would translate
Compared to the
? macro, the
?? macro checks that the entire expression is not
null, whereas the
? macro would just check that the preceding elements (e.g.
null before returning the default value.
The macro is also smart about what it checks for null, so anything that is
<: AnyVal will not be checked for null. For example
Would be translated to:
Here’s the result of running the included jmh benchmarks:
Data in tabular form
You can find the source code for the JMH benchmarks here. If you want to run the benchmarks yourself, just run
sbt bench, or
sbt quick-bench for a shorter run.
These benchmarks compare all of the known ways (or at least the ways that I know of) to handle null-safety in scala. It demonstrates that the explicit null-safety is the highest performing and that the ScalaNullSafe macro has equivalent performance.
In the next section we’ll examine how the usage of
null will evolve in the next major version of Scala, Scala 3, AKA Dotty.