Implicit conversions and null

  • A+

Following code

import scala.language.implicitConversions  object myObj {   implicit def nullToInt(x: Null) = 0    def main(args: Array[String]): Unit = {     val x = 1 + null     val y = 1 + nullToInt(null)      println(x + " " + y)   } } 

gives below result

1null 1 

I was expecting both vals to be Int and equal to 1.

Apparently first val is String and equals "1null".

Xprint:typer shows that source code is translated into

package <empty> {   import scala.language.implicitConversions;   object myObj extends scala.AnyRef {     def <init>(): myObj.type = {       myObj.super.<init>();       ()     };     implicit def nullToInt(x: Null): Int = 0;     def main(args: Array[String]): Unit = {       val x: String = 1.+(null);       val y: Int = 1.+(myObj.this.nullToInt(null));       scala.Predef.println(x.+(" ").+(y))     }   } } 

There are no symbolic methods for int which accept null

scala> 10+null res0: String = 10null  scala> 10*null <console>:12: error: overloaded method value * with alternatives:   (x: Double)Double <and>   (x: Float)Float <and>   (x: Long)Long <and>   (x: Int)Int <and>   (x: Char)Int <and>   (x: Short)Int <and>   (x: Byte)Int  cannot be applied to (Null)        10*null          ^  scala> 10-null <console>:12: error: overloaded method value - with alternatives:   (x: Double)Double <and>   (x: Float)Float <and>   (x: Long)Long <and>   (x: Int)Int <and>   (x: Char)Int <and>   (x: Short)Int <and>   (x: Byte)Int  cannot be applied to (Null)        10-null          ^ 

I assume both "1" and "null" were converted into String instead of applying implicit nullToInt. Can someone explain how compiler came up with that? What logic/workflow was used?

And another question is whether there is a way to enable implcit nullToInt?

PS. I'm not talking about best practices here. Feel free to consider a question as a matter of academic interest.


classOf[Any].getMethods foreach println indeed does not list +, so Daniel Hinojosa is right about + not being a method of Any.

Updated answer (or rather, a preliminary sketch of an answer[*])

As @DanielHinojasa has noticed, there is an implicit class

implicit final class any2stringadd[A] extends AnyVal  

defined in Predef.

The any2stringadd has the method + that is defined as follows:

def +(other: String): String  

Now one might wonder why the 1 is implicitly converted into any2stringadd, instead of null being converted into 0. Consider this paragraph from the Spec:

In a selection e.m(args) with e of type T, if the selector m denotes some member(s) of T, but none of these members is applicable to the arguments args. In this case a view v is searched which is applicable to e and whose result contains a method m which is applicable to args. The search proceeds as in the case of implicit parameters, where the implicit scope is the one of T. If such a view is found, the selection e.m is converted to v(e).m(args).

This quote explicitly specifies that in the expression e.m(a) that doesn't typecheck, the compiler will first attempt to implicitly convert the left operand e. Therefore, in the case of 1 + null, the following happens:

  1. 1 is of type Int, it has a method with name +, but none of the methods called + expect null as argument, so they are not applicable.
  2. Therefore, as specified in the quoted paragraph, the compiler attempts to find an implicit conversion into some other type that has method + that is applicable to Null.
  3. Since Int is a subtype of Any, the compiler finds any2stringadd.
  4. Because null is of type Null and Null is a subtype of String, the method + of any2stringadd is deemed applicable to the argument null, and so the value null is transformed into String "null", and concatenated with the String "1", giving "1null".

[*]TODO: Why is I not implicitly converted to S in

import language.implicitConversions; class I { def +++(i: I) = "I +++ I" };  class S { def +++(a: Any) = "S +++ Any" };  class N;  implicit def n2i(n: N): I = new I;  implicit def i2s(i: I): S = new S;  println((new I) +++ (new N)); 


TODO-2: What does "applicable" actually mean?


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: