简体   繁体   English

Scala解析多个隐式参数

[英]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 . 请注意, IntLike只是为了让我相信这不是一个专注于Int的问题。

This seems a fairly standard, if bad, use of implicits, and I was expecting it to work happily. 这似乎是一个相当标准,如果不好,使用implicits,我期待它愉快地工作。 However, on invoking purchaseCandles() the REPL yields the following error: 但是,在调用purchaseCandles() ,REPL会产生以下错误:

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 错误:模糊隐含值:两个值StringCanBuildFrom在对象Predef中的类型=> scala.collection.generic.CanBuildFrom [String,Char,String]和值类型为guest的类型匹配预期类型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. A必然会有一个IntLike的视图边界, IntLike是我刚刚发明的一种类型。 The REPL confirms that there is no implicit view available: REPL确认没有可用的隐式视图:

scala> implicitly[Tree => IntLike] scala>隐式[Tree => IntLike]

res14: Tree => IntLike = function1 res14:Tree => IntLike = function1

but

scala> implicitly[scala.collection.generic.CanBuildFrom[String, Char, String] => IntLike] 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. :18:错误:scala.collection.generic.CanBuildFrom [String,Char,String] => IntLike没有隐式视图。

So how can StringCanBuildFrom be of an appropriate type? 那么StringCanBuildFrom如何成为合适的类型呢? Is the compiler capable of resolving the multiple dependent implicits, and if not, why is this the error being shown? 编译器是否能够解析多个依赖的implicits,如果没有,为什么会显示错误?

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. 请注意,您正在调用purchaseCandles而没有任何关于类型A提示,我认为这是问题的根源。

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. StringCanBuildFrom看起来更像一个令人困惑的错误消息。 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. 如果您专注于conforms这里,为什么这不工作可能会变得更加清晰的理由: Predef.conform指出,每当你问的隐式转换A => A ,这是由身份来保证。 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 . 这样,如果你有def foo[B <% A](b: B) ,你总是可以用类型A的值调用foo ,而不需要定义任何类型的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 . 您要求解析未指定类型的隐式参数以及从未指定类型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()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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