NullSafe: Kotlin / Groovy flavored null-safe ? operator now in Scala

Recently I published NullSafe, a library to provide Kotlin / Groovy flavored null-safe ? operator.

Motivation

Null references are known as The Billion Dollar Mistake. Alternatively, Option is preferred in Scala. Unfortunately, JavaSE API and other Java libraries, which may produce null references, are still used in Scala development. This NullSafe library aims to handle null references in these use cases.

Installation

NullSafe is a part of Dsl.scala project, requiring Dsl.scala’s compiler plug-in:

libraryDependencies += "com.thoughtworks.dsl" %% "keywords-nullsafe" % "latest.release"

addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release")

Usage

You can use @ ? annotation to type a nullable value.

import com.thoughtworks.dsl.keywords.NullSafe._

case class Tree(left: Tree @ ? = null, right: Tree @ ? = null, value: String @ ? = null)

val root: Tree @ ? = Tree(
  left = Tree(
    left = Tree(value = "left-left"),
    right = Tree(value = "left-right")
  ),
  right = Tree(value = "right")
)

A normal . is not null safe, when performing a method on a null value.

a[NullPointerException] should be thrownBy {
  root.right.left.right.value
}

The above code throws an exception because root.right.left is null . The exception can be avoided by using ? operator on the nullable value instead:

root.?.right.?.left.?.value should be(null)

The entire expression is null if one of ? is performed on a null value.

The boundary of a null safe operator ? is the nearest enclosing expression whose type is annotated as @ ?.

("Hello " + ("world " + root.?.right.?.left.?.value)) should be("Hello world null")
("Hello " + (("world " + root.?.right.?.left.?.value.?): @ ?)) should be("Hello null")
(("Hello " + ("world " + root.?.right.?.left.?.value.?)): @ ?) should be(null)

Related works

For pure Scala code that produces Options, monads and Monadic !-notation can be used. Monadic is also a library in Dsl.scala project. An example of !-notation with cats’ Option monads can be found in https://scastie.scala-lang.org/Atry/m8NQ38cvR5yKANEGBttyPQ/1

Links

I like how this looks, but does the type that you want to us .? with have to be declared with @ ?? I guess I’d like to see an example of this working with a class declared in Java.

.? is available on all reference types, no matter if it annotated @ ? or not, unless it is explicitly wrapped in a NotNull. Check the “Note” section in the Scaladoc.

For example, a java.util.HashMap may produce nulls:

val myMap = new java.util.HashMap[String, String]();
((myMap.get("key1").? + myMap.get("key2").?): @ ?) should be(null)