2013-03-15 4 views
4

В попытке ответить на this question, я придумал следующий код:разрешение Scala нескольких неявных параметров

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

Обратите внимание, что IntLike только там, чтобы убедить меня, что это не проблема, сфокусированы на Int.

Это кажется довольно стандартным, если плохо, использование implicits, и я ожидал, что он будет работать с удовольствием. Однако, ссылаясь на purchaseCandles() РЕПЛ дает следующее сообщение об ошибке:

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

Я не могу за жизнь мне увидеть, как это бывает. A обязана иметь вид вида IntLike, тип, который я только что придумал. РЕПЛИ подтверждает, что не существует никакого неявного вида в наличии:

scala> implicitly[Tree => IntLike]

res14: Tree => IntLike = function1

но

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.

Так как может быть StringCanBuildFrom соответствующего типа? Является ли компилятор способным разрешать множественные зависимые импликации, а если нет, то почему это ошибка отображается?

ответ

1

Прежде чем приступить к ответу, здесь приведен первый случай. Обратите внимание, что вы вызываете purchaseCandles без каких-либо намеков на тип A, который, по моему мнению, является источником проблемы.

object Foo 

def test[A <% Foo.type](implicit bar: A) {} 

test 

Ошибка:

<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 
      ^

StringCanBuildFrom больше похоже на сообщение запутанной ошибки. Если вы сконцентрируетесь на conforms здесь, причина, по которой это не работает, может стать более понятным: Predef.conform заявляет, что всякий раз, когда вы запрашиваете неявное преобразование A => A, это гарантируется с помощью идентификации. Это так, что если у вас есть def foo[B <% A](b: B), вы всегда можете позвонить foo со значением типа A без необходимости определять любой вид A => A.


Вы просите разрешения неявной аргументе неопределенного типа вместе с переходом от этого неопределенного типа к IntLike. Я думаю, что это выходит за рамки неявного алгоритма разрешения. Тем не менее сообщение об ошибке несколько странно.

+0

Да , что выходит за рамки неявного разрешения, кажется вероятным, но сообщение запутывает. Все работает, если вы явно указываете параметр типа: 'test [Foo.type]' в вашем примере. – Impredicative

0

Еще нет объяснений, почему он не, хотя, как было сказано, я ожидаю, что это слишком много для неявного разрешения из-за неопределенного входного типа-, но вы можете заставить его работать так:

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() 
Смежные вопросы