20

Scala's Option class has an orNull method, whose signature is shown below.

orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1

I'm bewildered by the implicit thing. Would somebody please explain how it can be used, ideally with an example?

Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
David
  • 5,184
  • 3
  • 41
  • 67
  • 6
    Alas, the source code uses `Null <:< A1`, which makes it much more intuitive. Scaladoc's though, gets the type signature from the compiler, so these syntactic niceties are long gone by them. I'm talking to one of scaladoc committers about it, though. Let's see if this get improved on. – Daniel C. Sobral Dec 17 '10 at 10:49

5 Answers5

31
scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       Some(1).orNull
               ^
scala> (None : Option[Int]).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       (None : Option[Int]).orNull

scala> Some("hi").orNull
res21: java.lang.String = hi

scala> Some(null : String).orNull
res22: String = null

scala> (None : Option[String]).orNull
res23: String = null

To explain the implicit thing: orNull is a way of getting back from the Some|None idiom to Java's value|null idiom (which is, of course, bad). Now only AnyRef values (instances of classes) can accept a null value.

So what we would have liked is def orNull[A >: Null] = ..... But A is already set and we don't want to restrict it in the definition of the trait. Therefore, orNull expects an evidence that A is a nullable type. This evidence is in the form of an implicit variable (hence the name 'ev')

<:<[Null, A1] can be written as Null <:< A1 seeing it like this, it is similar to 'Null <: A1'. <:< is defined in Predef as well as the method that provides the implicit value named conforms.

I think the use of A1 is not strictly required here and is because orNull uses getOrElse (where the default given can be a super type of A)

scala> class Wrapper[A](option: Option[A]) {
     | def orNull(implicit ev: Null <:< A): A = if(option.isEmpty) null else option.get
     | }
defined class Wrapper

scala> new Wrapper(Some("hi")).orNull
res18: java.lang.String = hi
IttayD
  • 28,271
  • 28
  • 124
  • 178
  • 6
    Seeing stuff like that, it's actually hard not to love Scala. – Madoc Dec 17 '10 at 06:15
  • 4
    The library writers go a long way to make everything modular and work nicely; the downside is that the signatures often look absolutely weird for the outsider, and the error messages are unintelligible. A good tip might be to ignore it and just read the documentation. :-) – Dr. Hans-Peter Störr Dec 17 '10 at 08:42
5

orNull purpose is first of all in ensuring compatibility of Option with Java. Though usage of null is discouraged in Scala, some interfaces may expect to get nullable references.

orNull has a straightforward implementation:

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

According to this, null will be returned not only for boxed nulls (Some(null)), but also for None (e.g., if you call None.get, exception will be thrown).

Implicit parameter checks, if the boxed value is nullable.

Good usage example can be found right in the comments to orNull:

val initialText: Option[String] = getInitialText
val textField = new JComponent(initialText.orNull,20)
Aaron Novstrup
  • 20,967
  • 7
  • 70
  • 108
Vasil Remeniuk
  • 20,519
  • 6
  • 71
  • 81
  • 2
    Just to make this more explicit: this works only for reference types (subtypes of AnyRef) but not for value types (subtype of AnyVal), since they have no null value. – Dr. Hans-Peter Störr Dec 17 '10 at 08:20
  • So what if, instead of getInitialText, you had getInitialNum, returning Long? Suppose you then wanted to pass that long number to Java, or null if it was not defined. – David Dec 17 '10 at 19:37
  • 2
    Then you need to use type ascription, explicitly specifying `java.lang.Long` instead of `scala.Long`: `val initialText: Option[java.lang.Long] = getInitialLong` – Vasil Remeniuk Dec 17 '10 at 20:56
4

Remember that in Scala primitive types and reference types are unified - but only reference types are nullable. The implicit simply allows the compiler to confirm that A1 is a reference type.

Kris Nuttycombe
  • 4,560
  • 1
  • 26
  • 29
  • OK. Would you give me an example of its usage? – David Dec 17 '10 at 04:39
  • Cancel that; IttayD has provided an example. This section of "Programming Scala" seems relevant: http://programming-scala.labs.oreilly.com/ch12.html#NothingAndNull. – David Dec 17 '10 at 05:09
2

To understand why it is useful, IttayD provided a nice explanation:

So what we would have liked is def orNull[A >: Null] = ..... But A is already set and we don't want to restrict it in the definition of the trait. Therefore, orNull expects an evidence that A is a nullable type. This evidence is in the form of an implicit variable (hence the name 'ev')

In summary, type constraints are useful when you want have methods (eg orNull) on a generic class (eg Option) with more specific constraints (eg Null <: A <: Any) than on the class itself (eg A <: Any).

This is another "feature" that is not built into the language but comes for free thanks to implicit parameters and variance annotations of type parameters. To understand this, look at the definition of <:<:

// from Predef      
sealed abstract class <:<[-From, +To] extends (From => To)
implicit def conforms[A]: A <:< A = new (A <:< A) {def apply(x: A) = x}

For

scala> Some(1).orNull
<console>:10: error: could not find implicit value for parameter ev: <:<[Null,Int]
       Some(1).orNull

the compiler looks for an implicit value of type <:<[Null, Int] and will find the method def conforms[A]: A <:< A. So there has to be an A for which <:<[A, A] conforms to <:<[Null, Int]. There is no A for which this holds and as a result the compiler will complain about the missing implicit value.

However, for

scala> Some("hi").orNull
res21: java.lang.String = hi

we are lucky. Now, the compiler tries to find an A for which <:<[A, A] conforms to <:<[Null, String]. This works for A = String, because Null is a subtype of String and the From type parameter of the class <:< is defined as contravariant).

As mentioned, the most intuitive way to think about type constraints is reading it like a type bound (i.e. reading it as Null <: Int). Null does not conform to Int and there is no implicit value for <:<[Null, Int]. On the other hand, Null does conform to String and the compiler will find the implicit parameter.

By the way, here is another related answer.

Community
  • 1
  • 1
weberste
  • 1,884
  • 2
  • 17
  • 12
0

Re : 'how' is this used - one place we are finding this useful is when dealing with java api mappings where null is commonplace, e.g. on jdbc prepared statements to nullable sql columns. The Optional internal model fields can be mapped:

stmt.setDate("field", myModel.myDateField.orNull)

Instead of the more verbose:

stmt.setDate("field", myModel.myDateField.getOrElse(null))
StuartLC
  • 104,537
  • 17
  • 209
  • 285