1
votes

Using the Scala REPL, I've defined a function that takes an Int as its first parameter and a function with this signature Int => Int as the second implicit parameter:

scala> def doer(i: Int)(implicit a: Int => Int): Int = a(i)
doer: (i: Int)(implicit a: Int => Int)Int

Why does running this function without providing the implicit parameter work?

scala> doer(4)
res1: Int = 4

Where does the implicit Int to Int function come from? The REPL reports that there are no implicits defined:

scala> :impl
No implicits have been imported other than those in Predef.
4
To avoid this kind of problem I only allow implicit parameters which use at least one type that is defined by my code. This means that there is no danger of confusion with implicit values defined in other libraries. - Tim

4 Answers

2
votes

Predef contains an implicit evidence that one type is a subtype of another: A <:< B. Every type is a subtype of itself so implicitly[Int <:< Int] works. This <:< class extends function A => B. So that's why implicitly[Int => Int] also works.

Int and java.lang.Integer however are different things with no subtype relation whatsoever, so these int2Integer implicits have nothing to do with it.

If you have a REPL of a recent Scala version you can type

scala> doer(4) //print

And press the tab key instead of enter. It will show you the desugared version doer(4)(scala.Predef.$conforms[Int]) of your code with all implicits filled in explicitly.

0
votes

There are bunch of implicits defined in scala.Predef which are accessible in all Scala compilation units without explicit qualification, and one of them is

implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer]
0
votes

If you check the Predef source code it is possible to see a lot of implicit functions. The compiler will just pick up a compatible one.

Some examples:

  implicit def booleanArrayOps(xs: Array[Boolean]): ArrayOps[Boolean] = new ArrayOps.ofBoolean(xs)
  implicit def byteArrayOps(xs: Array[Byte]): ArrayOps[Byte]          = new ArrayOps.ofByte(xs)
  implicit def charArrayOps(xs: Array[Char]): ArrayOps[Char]          = new ArrayOps.ofChar(xs)
  implicit def doubleArrayOps(xs: Array[Double]): ArrayOps[Double]    = new ArrayOps.ofDouble(xs)
  implicit def floatArrayOps(xs: Array[Float]): ArrayOps[Float]       = new ArrayOps.ofFloat(xs)
  implicit def intArrayOps(xs: Array[Int]): ArrayOps[Int]             = new ArrayOps.ofInt(xs)
  implicit def longArrayOps(xs: Array[Long]): ArrayOps[Long]          = new ArrayOps.ofLong(xs)
  implicit def refArrayOps[T <: AnyRef](xs: Array[T]): ArrayOps[T]    = new ArrayOps.ofRef[T](xs)
  implicit def shortArrayOps(xs: Array[Short]): ArrayOps[Short]       = new ArrayOps.ofShort(xs)
  implicit def unitArrayOps(xs: Array[Unit]): ArrayOps[Unit]          = new ArrayOps.ofUnit(xs)

  // "Autoboxing" and "Autounboxing" ---------------------------------------------------

  implicit def byte2Byte(x: Byte)           = java.lang.Byte.valueOf(x)
  implicit def short2Short(x: Short)        = java.lang.Short.valueOf(x)
  implicit def char2Character(x: Char)      = java.lang.Character.valueOf(x)
  implicit def int2Integer(x: Int)          = java.lang.Integer.valueOf(x)
  implicit def long2Long(x: Long)           = java.lang.Long.valueOf(x)
  implicit def float2Float(x: Float)        = java.lang.Float.valueOf(x)
  implicit def double2Double(x: Double)     = java.lang.Double.valueOf(x)
  implicit def boolean2Boolean(x: Boolean)  = java.lang.Boolean.valueOf(x)

  implicit def Byte2byte(x: java.lang.Byte): Byte             = x.byteValue
  implicit def Short2short(x: java.lang.Short): Short         = x.shortValue
  implicit def Character2char(x: java.lang.Character): Char   = x.charValue
  implicit def Integer2int(x: java.lang.Integer): Int         = x.intValue
  implicit def Long2long(x: java.lang.Long): Long             = x.longValue
  implicit def Float2float(x: java.lang.Float): Float         = x.floatValue
  implicit def Double2double(x: java.lang.Double): Double     = x.doubleValue
  implicit def Boolean2boolean(x: java.lang.Boolean): Boolean = x.booleanValue
0
votes

Implicits are designed for such purpose. The compiler will search if any implicit definition objects are available. If it finds then will use them. There are many implicit definition objects provided scala. You can also define the custom objects to be implicit and will be used if they are within the scope of your code.

See the doc https://docs.scala-lang.org/tour/implicit-parameters.html

doer works because since it accepts implicit parameter, it is available in Predef. Predef is imported automatically to scala scope. Looks like the function used is int2Integer.