Regarding following piece of code (scala 2.12.10
) my question is why order matter in that specific case for implicit arguments. I also doesn't understand why compiler tells me that there is ambiguous implicit values, i see any of hint about it.
import scala.reflect.ClassTag
trait Wrapper[T] {
implicit def ct: ClassTag[T]
// First try using scala syntaxic sugar for single type parameter type class
def asPair1[K : ClassTag, V : ClassTag](implicit ev: T <:< (K, V)): Unit = Unit
// Same as asPair1 with explicit implicits ClassTag, in same order than with syntaxic sugar version
def asPair1Bis[K, V](implicit kt: ClassTag[K], vt: ClassTag[V], ev: T <:< (K, V)): Unit = Unit
// Workaround
def asPair2[K, V](implicit ev: T <:< (K, V), kt: ClassTag[K], vt: ClassTag[V]): Unit = Unit
}
trait Test[K, V] {
implicit def kt: ClassTag[K]
implicit def vt: ClassTag[V]
val w: Wrapper[(K, V)]
w.asPair1 // Fails
/**
error: ambiguous implicit values:
both method kt in trait Test of type => reflect.this.ClassTag[K]
and method vt in trait Test of type => reflect.this.ClassTag[V]
match expected type reflect.this.ClassTag[K]
w.asPair1 // Fails
^
error: No ClassTag available for K
w.asPair1 // Fails
^
error: not enough arguments for method asPair1: (implicit evidence$1: reflect.this.ClassTag[K], implicit evidence$2: reflect.this.ClassTag[V], implicit ev: $less$colon$less[scala.this.Tuple2[K,V],scala.this.Tuple2[K,V]])scala.this.Unit.
Unspecified value parameters evidence$1, evidence$2, ev.
w.asPair1 // Fails
*/
w.asPair1Bis // Fails
w.asPair2 // Works
val w2: Wrapper[(Int, Double)]
w2.asPair1 // Fails with exact same logs than with `w`
w2.asPair2
}
So first of all, do not mix context bounds and implicits, that is considered a bad practice; see this for more context.
Second, the order matters because if the ev
comes first then the type parameters K
and V
are solved by the compiler before it tries to search their ClassTags.
In the other case, the compiler has no info about what K
and V
are so it will try to just assume anything it can find, thus finding two possible implicit ClassTags (both the one for the outer K
and the outer V
) creating an ambiguity.