This is a simple example:
object CoerciveCovariance {
trait Cov[+T]
def cast[A, B](v: Cov[A])(
implicit
ev: A <:< B
) = {
v: Cov[B]
}
}
It doesn't compile:
CoerciveCovariance.scala:11:5: Found: (v : xxx.CoerciveCovariance.Cov[A])
Required: xxx.CoerciveCovariance.Cov[B]
one error found
Is it very hard to make to compiler to figure out the missing coercive upcasting from Cov[A] to Cov[B]? Why is it not the default behaviour?
Because type inference and implicit resolution are different.
<:and+belong to type inference,<:<belongs to implicit resolution.They make impact to each other. Indeed, type inference makes impact to implicit resolution
Here firstly type
Ais inferred to beInt(orString) and then it's checked that there is an implicit forInt(and no implicit forString).Similarly, implicit resolution makes impact to type inference
Here the type
Stringwas inferred from the type class having the only instance.So type inference and implicit resolution make impact to each other but are different.
If
A <: BthenA <:< Bbut if
A <:< Bthen not necessarilyA <: B<:is checked by the compiler according to the spec https://scala-lang.org/files/archive/spec/2.13/03-types.html#conformance<:<is just a type classwith the only instance
So
<:<doesn't have many properties of an order. By default there is no transitivityno antisymmetry
no monotonicity
Although starting from Scala 2.13 the following methods are defined in the standard library
but they do not define implicits. So if you need these properties you can define transitivity
Antisymmetry is trickier
If you resolve implicits manually
... = implicitly[A =:= B](antisym[A, B]), you'll see the reason (althoughimplicitly[A =:= B](antisym[A, B](ev2, ev3))works)So you have to resolve this ambiguity prioritizing implicits. You can't decrease the priority of implicit parameter
ev2. So you have to decrease the priority ofantisym, which is your implicit in the current scope, you can't put it to the implicit scope (companion object etc.). The only way I found is withshapeless.LowPrioritySimilarly you can define monotonicity
But if you put all instances into the scope you'll have compile-time
StackoverflowSo I guess you see why those methods are not defined as implicits by default. This would pollute the implicit scope.
More about the difference
<:vs.<:<https://blog.bruchez.name/posts/generalized-type-constraints-in-scala/Besides (compile-time) type class
<:<there is also (runtime) method<:<fromscala-reflectScala 2.13.10.
Ensure arguments to generic method have same type in trait method
What is the implicit resolution chain of `<:<`
Type parameter under self-type doesn't conform to upper bound despite evidence
Covariance type parameter with multiple constraints
How to do type-level addition in Scala 3?