scala-logo-small

Does Scala have the Safe Navigation / Null Conditional Operator?

Most languages allow null values but then don’t provide good features for handling them. Java is one such language even though Java applications are notorious for having ubiquitous NullPointerExceptions.

Other languages provide different features for null handling. One such mechanism is the safe navigation operator in Groovy (see section 6.1 of the documentation). It is also known as the null conditional operator in C#. The safe navigation operator allows short-circuiting member accesses. In the event that a member is null the call ends without throwing an NPE and returns null instead.

The Groovy documentation has this example:

  
  //Groovy
  def person = Person.find { it.id == 123 }    
  def name = person?.name // if person is null
  assert name == null // then this assert is true

Of course Scala doesn’t have this feature because pure idiomatic Scala applications don’t have null values. Scala rightfully takes the opinion that null values should not exist. Unfortunately it also takes the opinion that Java compatibility is important and so null values are re-introduced when the application refers to Java code.

Usually Scala prefers a functional approach and the use of the disjoint union type Option over null. The Option type represents the presence or absence of a value in a type-safe way. Thus, the Groovy documentation’s example rewritten in Scala would be:

  // Scala
  val person: Option[Person] = Person.find { id == 123 }    
  val name = person.map(_.name) // person is None so name is None also
  assert(name == None) // then this assert is true

Option types are further explained in Programming in Scala section 15.6.

For the sake of argument, let’s say that there is a Scala application that uses a Java version of the Person class. And let’s further say that Person.find returns null in that case. Set aside for the moment the existence of Java 8’s Optional type, which is supposed to be roughly equivalent to Scala’s Option type.

In that case, out-of-the-box Scala would have to handle the possible null result of Person.find by wrapping it in an Option. When a null is returned, calling “Option(null)” results in a None. The example would become:

  // Scala
  val person = Option(Person.find { id == 123 })
  val name = person.map(_.name) // person is None so name is None also
  assert(name == None) // then this assert is true

If you are really married to the safe navigation operator, you can write a Scala implicit that will achieve nearly the same result:

  //Scala
  implicit class NullConditional[A,B](val a: A) extends AnyVal {
    def ?(f: A => B): Option[B] = { Option(a).map(f) }
  }

With that implicit in scope, the example could become:

  // Scala
  val person = Person.find { id == 123 } // if person is null
  val name = person?(_.name) // this will return None
  assert(name == None) // true

Leave a Reply

Your email address will not be published. Required fields are marked *