Question

I'm defining some Scala implicits to make working with a particular unchangeable set of Java classes easier. The following Scala code is a simplified example that obviously looks crazy, in the real world I'm trying to grab particular resources (rather than numeric age) implicitly from the Monkey, Tree & Duck for use in various methods like purchaseCandles():

// actually 3 Java classes I can not change:
case class Monkey(bananas: Int) 
case class Tree(rings: Int)
case class Duck(quacks: Seq[String])

// implicits I created to make my life easier...
implicit def monkey2Age(monkey: Monkey): Int = monkey.bananas / 1000
implicit def tree2Age(tree: Tree): Int = tree.rings
implicit def duck2Age(duck: Duck): Int = duck.quacks.size / 100000

// one of several helper methods that I would like to define only once,
// only useful if they can use an implicit parameter.
def purchaseCandles()(implicit age: Int) = {
  println(s"I'm going to buy $age candles!")
}

// examples of usage
{
  implicit val guest = Monkey(10000)

  purchaseCandles()
}

{
  implicit val guest = Tree(50)

  purchaseCandles()
}

{
  implicit val guest = Duck(Seq("quack", "quack", "quack"))

  purchaseCandles()
}

The compiler error, which occurs 3 times:

could not find implicit value for parameter age: Int 
purchaseCandles()
               ^

Leaving aside the many different ways in which this sample code is crazy, my real question is: can implicit conversions of implicit values satisfy implicit parameters in Scala?

Was it helpful?

Solution

Short answer: no. Scala's compiler will only ever look to apply a single implicit, so if it fails to spot an implicit int lying around, it will stop and give up.

However, you could write your purchaseCandles method to operate on types that can be converted to an Int, and require a parameter of that type:

def purchaseCandles[A <% Int]()(implicit age : A) = {
  val asAge : Int = age
  println(s"I'm going to buy $asAge candles!")
}

The asAge part is necessary to force the application of the implicit conversion.

As of yet, I seem to need to specify the type of A in this scenario, though I can't work out why: since there shouldn't be other values around of types that can be implicitly converted to Int (this happens with brand new types as well, so it's not the ubiquity of Int.) But you can do:

{
  implicit val guest = Monkey(10000)

  purchaseCandles[Monkey]()
}

This use of implicits, however, is probably a bad idea!

OTHER TIPS

You actually can do that: You just have to mark the parameters of your implicit conversion as implicit as well:

implicit def monkey2Age(implicit monkey: Monkey): Int = monkey.bananas / 1000
implicit def tree2Age(implicit tree: Tree): Int = tree.rings
implicit def duck2Age(implicit duck: Duck): Int = duck.quacks.size / 100000

This will chain the implicits they way you want.

As always: Beware, it will also do so in places you don't want it to. By the way, I strongly advise against an implicit parameter of type Int (or an implicit value thereof). It is just too generic. (I'm somewhat assuming this is just like that in your example).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top