Reputation: 5069
In trying to answer this question, I came up with the following code:
case class Monkey(bananas: Int)
case class Tree(rings: Int)
case class Duck(quacks: Seq[String])
implicit class IntLike(val x : Int) extends AnyVal
implicit def monkey2Age(monkey: Monkey): IntLike = monkey.bananas / 1000
implicit def tree2Age(tree: Tree): IntLike = tree.rings
implicit def duck2Age(duck: Duck): IntLike = duck.quacks.size / 100000
def purchaseCandles[A <% IntLike]()(implicit age : A) = {
val asAge : IntLike = age
println(s"I'm going to buy $asAge candles!")
}
{
implicit val guest = Tree(50)
purchaseCandles()
}
Note that the IntLike
is only there to convince me this wasn't a problem focussed on Int
.
This seems a fairly standard, if bad, use of implicits, and I was expecting it to work happily. However, on invoking purchaseCandles()
the REPL yields the following error:
error: ambiguous implicit values: both value StringCanBuildFrom in object Predef of type => scala.collection.generic.CanBuildFrom[String,Char,String] and value guest of type Tree match expected type A
I cannot for the life of me see how this is the case. A is bound to have an view bound of IntLike
, a type I just invented. The REPL confirms that there is no implicit view available:
scala> implicitly[Tree => IntLike]
res14: Tree => IntLike = function1
but
scala> implicitly[scala.collection.generic.CanBuildFrom[String, Char, String] => IntLike]
:18: error: No implicit view available from scala.collection.generic.CanBuildFrom[String,Char,String] => IntLike.
So how can StringCanBuildFrom
be of an appropriate type? Is the compiler capable of resolving the multiple dependent implicits, and if not, why is this the error being shown?
Upvotes: 4
Views: 1452
Reputation: 67330
Still no explanation why it fails—although, as said, I expect this is too much for the implicit resolution because of the undefined input type—, but you can make it work like this:
implicit def findIntLike[A <% IntLike](x: A): IntLike = x
def purchaseCandles()(implicit age: IntLike) = {
println(s"I'm going to buy $age candles!")
}
implicit val guest = Tree(50)
purchaseCandles()
Upvotes: 0
Reputation: 67330
Before attempting an answer, here is first the reduced case. Note that you are calling purchaseCandles
with no hint whatsoever on type A
which I think is the origin of the problem.
object Foo
def test[A <% Foo.type](implicit bar: A) {}
test
Error:
<console>:10: error: ambiguous implicit values:
both value StringCanBuildFrom in object Predef of type =>
scala.collection.generic.CanBuildFrom[String,Char,String]
and method conforms in object Predef of type [A]=> <:<[A,A]
match expected type A
test
^
The StringCanBuildFrom
seems more like a confusing error message. If you concentrate on conforms
here, the reason why this doesn't work might become more clear: Predef.conform
states that whenever you ask for an implicit conversion A => A
, this is guaranteed by means of identity. This is so that if you have def foo[B <% A](b: B)
, you can always call foo
with a value of type A
without the need to define any sort of A => A
.
You are asking for the resolution of implicit argument of an unspecified type along with a conversion from that unspecified type to IntLike
. I think that is beyond the scope of the implicit resolution algorithm. Still the error message is somewhat strange.
Upvotes: 1