Reputation: 437
I have the following code in Scala. Let's say there's an implicit conversion of A to B. Why doesn't my implicit List[A] to List[B] conversion work?
object ImplicitTest {
case class CaseClassA(a: String)
case class CaseClassB(b: String)
implicit def a2b(a: CaseClassA): CaseClassB = CaseClassB(a.a)
implicit def listImplicitConversion[A, B](l: List[A])(implicit conv: A => B): List[B] = l.map(conv)
def main(args: Array[String]) {
val listOfA: List[CaseClassA] = List(CaseClassA("a"))
val listOfB: List[CaseClassB] = listOfA
}
}
Compiling this code results in the following error:
Error:(14, 37) type mismatch;
found : List[ImplicitTest.CaseClassA]
required: List[ImplicitTest.CaseClassB]
val listOfB: List[CaseClassB] = listOfA
Upvotes: 4
Views: 167
Reputation: 149598
This is a bug, namely SI-10405, which I've opened some time ago.
For some reason, Predef
is causing the implicit search to fail. If you compile your code with the -Yno-predef
flag, you'll succeed.
The symptom is that you see the typer trying to resolve the implicit via Predef.conforms[A]
when compiling under -Xlog-implicits
:
λ scalac -Xlog-implicits Bar.scala
Bar.scala:16: listImplicitConversion is not a valid implicit value for List[yuval.tests.ImplicitTest.CaseClassA] => List[yuval.tests.ImplicitTest.CaseClassB] because:
incompatible: (l: List[yuval.tests.ImplicitTest.CaseClassA])(implicit conv: yuval.tests.ImplicitTest.CaseClassA => yuval.tests.ImplicitTest.CaseClassA)List[yuval.tests.ImplicitTest.CaseClassA] does not match expected type List[yuval.tests.ImplicitTest.CaseClassA] => List[yuval.tests.ImplicitTest.CaseClassB]
val listOfB: List[CaseClassB] = listOfA
And ignores any further search, although it does previously succeed to match:
|-- List[CaseClassB] TYPEmode (site: value listOfB in ImplicitTest)
| | | | | |-- scala.`package` EXPRmode-POLYmode-QUALmode (site: value listOfB in ImplicitTest)
| | | | | | \-> scala.type
| | | | | |-- CaseClassB TYPEmode (site: value listOfB in ImplicitTest)
| | | | | | \-> yuval.tests.ImplicitTest.CaseClassB
| | | | | \-> List[yuval.tests.ImplicitTest.CaseClassB]
| | | | |-- listOfA : pt=List[yuval.tests.ImplicitTest.CaseClassB] BYVALmode-EXPRmode (site: value listOfB in ImplicitTest)
| | | | | [search #2] start `List[yuval.tests.ImplicitTest.CaseClassA]`, searching for adaptation to pt=List[yuval.tests.ImplicitTest.CaseClassA] => List[yuval.tests.ImplicitTest.CaseClassB] (silent: value listOfB in ImplicitTest) implicits disabled
| | | | | [search #2] considering listImplicitConversion
| | | | | |-- listImplicitConversion BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value listOfB in ImplicitTest) implicits disabled
| | | | | | [adapt] [A, B](l: List[A])(implicit conv: A => B)List[B] adapted to [A, B](l: List[A])(implicit conv: A => B)List[B]
| | | | | | \-> (l: List[A])(implicit conv: A => B)List[B]
| | | | | solving for (A: ?A, B: ?B)
| | | | | [search #3] start `[A, B](l: List[A])(implicit conv: A => B)List[B]` inferring type B, searching for adaptation to pt=yuval.tests.ImplicitTest.CaseClassA => B (silent: value listOfB in ImplicitTest) implicits disabled
| | | | | [search #3] considering a2b
| | | | | |-- { ((a: yuval.tests.ImplicitTest.CaseClassA) => ImplicitTe... : pt=yuval.tests.ImplicitTest.CaseClassA => ? EXPRmode (silent: value listOfB in ImplicitTest) implicits disabled
| | | | | | |-- ((a: yuval.tests.ImplicitTest.CaseClassA) => ImplicitTest... : pt=yuval.tests.ImplicitTest.CaseClassA => ? EXPRmode (silent: value listOfB in ImplicitTest) implicits disabled
| | | | | | | |-- ImplicitTest.this.a2b(a) EXPRmode (silent: value $anonfun in ImplicitTest) implicits disabled
| | | | | | | | |-- ImplicitTest.this.a2b BYVALmode-EXPRmode-FUNmode-POLYmode (silent: value $anonfun in ImplicitTest) implicits disabled
| | | | | | | | | \-> (a: yuval.tests.ImplicitTest.CaseClassA)yuval.tests.ImplicitTest.CaseClassB
| | | | | | | | |-- a : pt=yuval.tests.ImplicitTest.CaseClassA BYVALmode-EXPRmode (silent: value $anonfun in ImplicitTest) implicits disabled
| | | | | | | | | \-> yuval.tests.ImplicitTest.CaseClassA
| | | | | | | | \-> yuval.tests.ImplicitTest.CaseClassB
| | | | | | | \-> yuval.tests.ImplicitTest.CaseClassA => yuval.tests.ImplicitTest.CaseClassB
| | | | | | \-> yuval.tests.ImplicitTest.CaseClassA => yuval.tests.ImplicitTest.CaseClassB
| | | | | [adapt] a2b adapted to { ((a: yuval.tests.ImplicitTest.CaseClassA) => ImplicitTe... based on pt yuval.tests.ImplicitTest.CaseClassA => B
| | | | | [search #3] solve tvars=?B, tvars.constr= >: yuval.tests.ImplicitTest.CaseClassB
| | | | | solving for (B: ?B)
| | | | | [search #3] success inferred value of type yuval.tests.ImplicitTest.CaseClassA => =?yuval.tests.ImplicitTest.CaseClassB is SearchResult({
| | | | | ((a: yuval.tests.ImplicitTest.CaseClassA) => ImplicitTest.this.a2b(a))
| | | | | }, TreeTypeSubstituter(List(type B),List(yuval.tests.ImplicitTest.CaseClassB)))
If you're scared of all the output, this is the relevant part:
[search #3] success inferred value of type yuval.tests.ImplicitTest.CaseClassA => =?yuval.tests.ImplicitTest.CaseClassB is SearchResult({
((a: yuval.tests.ImplicitTest.CaseClassA) => ImplicitTest.this.a2b(a))
}, TreeTypeSubstituter(List(type B),List(yuval.tests.ImplicitTest.CaseClassB)))
Upvotes: 4