Может ли неявное преобразование неявного значения удовлетворить неявный параметр?

Я определяю некоторые последствия Scala, чтобы упростить работу с определенным неизменяемым набором классов Java. Следующий код Scala - это упрощенный пример, который явно выглядит сумасшедшим, в реальном мире я пытаюсь неявно получить определенные ресурсы (а не числовой возраст) от Monkey, Tree & Duck для использования в различных методах, таких как 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()
}

Ошибка компилятора, возникающая 3 раза:

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

Оставляя в стороне множество разных способов, которыми этот пример кода является сумасшедшим, мой реальный вопрос: могут ли неявные преобразования неявных значений удовлетворить неявные параметры в Scala?

2 ответа

Решение

Краткий ответ: нет. Компилятор Scala будет когда-либо пытаться применить только один неявный, поэтому, если он не обнаружит implicit int валяется, он остановится и сдастся.

Тем не менее, вы могли бы написать свой purchaseCandles метод для работы с типами, которые могут быть преобразованы в Intи требует параметр этого типа:

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

asAge часть необходима для принудительного применения неявного преобразования.

На данный момент, мне кажется, нужно указать тип A в этом сценарии, хотя я не могу понять, почему: поскольку не должно быть других значений вокруг типов, которые могут быть неявно преобразованы в Int (это происходит с совершенно новыми типами, так что это не повсеместное распространение Int.) Но вы можете сделать:

{
  implicit val guest = Monkey(10000)

  purchaseCandles[Monkey]()
}

Это использование имплицитов, однако, вероятно, плохая идея!

Вы действительно можете сделать это: вам просто нужно пометить параметры вашего неявного преобразования как неявные:

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

Это будет связывать последствия, которые вы хотите.

Как всегда: будьте осторожны, он также будет делать это там, где вы этого не хотите. Кстати, я настоятельно рекомендую неявный параметр типа Int (или его неявное значение). Это просто слишком общий характер. (Я предполагаю, что это так же, как в вашем примере).

Другие вопросы по тегам