2

Even after learning about static overloading in Scala (What is Scala's static overloading rule?), I still am unable to use it for the <:< class.
This class is used to check for subclassing, and is defined in Predef:

sealed abstract class <:<[-From, +To] extends (From => To) with Serializable
private[this] final val singleton_<:< = new <:<[Any,Any] { def apply(x: Any): Any = x }

However when I write A <:< B I am not sure if I am using an instance of <:<[A,A] or<:<[B,B], because following the static overloading rule neither is more specific then the other.

We first try this just to be sure it really works, replacing <:< with my mycic:

class superC

class subC extends superC

abstract class mycic[-From,+To] extends ((From) => To) with Serializable

object Main extends App {
    implicit def FandT[A]: mycic[A,A] = new (mycic[A,A]) { def apply(x: A) ={x}}
    val e = implicitly[subC mycic superC]
    val a = new subC
    e.apply(a)
}

This runs nicely. But when we try to define which one it is using:

class superC

class subC extends superC

abstract class mycic[-From,+To] extends ((From) => To) with Serializable

object Main extends App {
    implicit val FF : mycic[subC,subC] = new(mycic[subC,subC]){def apply(x:subC) ={println("FF");x}}
    implicit val TT : mycic[superC,superC] = new(mycic[superC,superC]){def apply(x:superC) ={println("TT");x}}
    val e = implicitly[subC mycic superC]
    val a = new subC
    e.apply(a)
}

we get the following compilation error:

Main.scala:10: error: ambiguous implicit values:
 both value TT in object Main of type => mycic[superC,superC]
 and value FF in object Main of type => mycic[subC,subC]
 match expected type mycic[subC,superC]
    val e = implicitly[subC mycic superC]
                          ^

Which implicit instance of mycic is it using in the code that runs? Why does it behave differently in the second example?

Community
  • 1
  • 1
user2759511
  • 640
  • 7
  • 16

1 Answers1

1

You can use print-tab completion to see types:

scala> def f[A,B](a: A, b: B)(implicit ev: A <:< B) = ev
f: [A, B](a: A, b: B)(implicit ev: <:<[A,B])<:<[A,B]

scala> trait X ; trait Y extends X
defined trait X
defined trait Y

scala> f(null.asInstanceOf[Y], null.asInstanceOf[X]) //print

$line3.$read.$iw.$iw.f[Y, X](null.asInstanceOf[$line4.$read.$iw.$iw.Y], null.asInstanceOf[$line4.$read.$iw.$iw.X])(scala.Predef.$conforms[Y]) // : <:<[Y,X]

(Scroll right to witness scala.Predef.$conforms[Y] as the implicit call.)

The conforms method is just an identity function. Because of variance of function:

scala> implicitly[(Y => Y) <:< (Y => X)]
res0: <:<[Y => Y,Y => X] = <function1>

scala> f(null.asInstanceOf[Y], null.asInstanceOf[X])
res1: <:<[Y,X] = <function1>

scala> .apply(null.asInstanceOf[Y])
res2: X = null

So if Y conforms to X, I can use Y => Y where you asked for Y => X. Similarly for <:<.

The ambiguous example is because neither of the two implicit vals conforms to the other (because of the contra- and co-variance of the type params). Otherwise, one val would have been picked as more specific.

Edit: You can use -Ytyper-debug to observe type inference, which is how it picks a type arg that is not specified. Here, it infers $conforms[Y] because the expected type is <:<[Y,X]:

|    |    |    |    solving for (A: ?A, B: ?B)
|    |    |    |    [search #1] start `[A, B](a: A, b: B)(implicit ev: <:<[A,B])<:<[A,B]`, searching for adaptation to pt=<:<[Y,X] (silent: value res0  in $iw) implicits disabled
|    |    |    |    [search #1] considering $conforms
|    |    |    |    solving for (A: ?A)
|    |    |    |    [adapt] $conforms adapted to [A]=> <:<[A,A] based on pt <:<[Y,X]
|    |    |    |    [search #1] success inferred value of type <:<[Y,X] is SearchResult(scala.Predef.$conforms[Y], )
|    |    |    |    |-- [A, B](a: A, b: B)(implicit ev: <:<[A,B])<:<[A,B] EXPRmode (site: value res0  in $iw) 
|    |    |    |    |    \-> <:<[Y,X]

If you specify f[X,X], it will similarly use $conforms[X].

som-snytt
  • 39,429
  • 2
  • 47
  • 129
  • sorry, but could not understand your answer. By the type being scala.Predef.$conforms[Y] are you answering my first question by stating that the implicit found is the <:<[Y,Y] one that means, the subclass one? If yes, why this one and not the other? I agree you can use Y => Y where it is asked for Y => X but can't you use just as well X => X? What breaks the symmetry? – user2759511 Aug 25 '16 at 13:10
  • @user2759511 not sure why you're still confused, but this similar wordier answer might help. http://stackoverflow.com/questions/36035759/how-does-the-scala-compiler-synthesize-implicit-evidence-with – som-snytt Oct 16 '16 at 21:50
  • I think you're confusing type inference (what type arg to supply by inference) with overloading (there's no overloading with <:<). – som-snytt Oct 16 '16 at 21:54