Please explain use of Option's orNull method

后端 未结 5 758
北荒
北荒 2021-02-01 21:29

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

orNull [A1 >: A](implicit ev : <:<[Null, A1]) : A1
5条回答
  •  日久生厌
    2021-02-01 21:40

    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
    :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.

提交回复
热议问题