简体   繁体   中英

Scala resolution of multiple implicit parameters

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?

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.

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM