从非协变类型类的实例创建协变类型类的实例

16

假设我有一个简单的类型类(type class),其实例会给我某种类型(type)的值:

trait GiveMeJustA[X] { def apply(): X }

我有一些实例:

case class Foo(s: String)
case class Bar(i: Int)

implicit object GiveMeJustAFoo extends GiveMeJustA[Foo] {
  def apply() = Foo("foo")
}

implicit object GiveMeJustABar extends GiveMeJustA[Bar] {
  def apply() = Bar(13)
}

现在我有一个类似的(但不相关)类型类,它做相同的事情,但其类型参数是协变的:

trait GiveMeA[+X] { def apply(): X }

在其伴生对象中,我们告诉编译器如何从非协变类型类的实例创建实例:

object GiveMeA {
  implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] =
    new GiveMeA[X] { def apply() = giveMe() }
}

现在我期望implicitly[GiveMeA[Foo]]可以编译通过,因为我们只有一个方法可以获得GiveMeA[Foo],但它并没有(至少在2.10.4或2.11.2上不行):

scala> implicitly[GiveMeA[Foo]]
<console>:16: this.GiveMeA.fromGiveMeJustA is not a valid implicit value for GiveMeA[Foo] because:
hasMatchingSymbol reported error: ambiguous implicit values:
 both object GiveMeJustAFoo of type GiveMeJustAFoo.type
 and object GiveMeJustABar of type GiveMeJustABar.type
 match expected type GiveMeJustA[X]
              implicitly[GiveMeA[Foo]]
                        ^
<console>:16: error: could not find implicit value for parameter e: GiveMeA[Foo]
              implicitly[GiveMeA[Foo]]
                        ^

如果我们删除无关的 GiveMeJustA 实例,它就可以工作:

scala> implicit def GiveMeJustABar: List[Long] = ???
GiveMeJustABar: List[Long]

scala> implicitly[GiveMeA[Foo]]
res1: GiveMeA[Foo] = GiveMeA$$anon$1@2a4f2dcc

尽管我们无法将GiveMeA.fromGiveMeJustA应用于此实例以获取GiveMeA[Foo](或任何GiveMeA[Foo]的子类型),但仍然存在这种情况。

我认为这看起来像是一个bug,但也有可能我漏掉了什么。这有任何意义吗?是否有合理的解决方法?

1个回答

2
我不明白为什么它能够正常工作,但是以下代码在当前情况下成功地解决了隐式问题(至少在scala v-2.10.1上)。然而,这仍然不能解释为什么你的示例一开始没有起作用:
我们将隐式GiveMeA[X]实例更改为搜索隐式GiveMeJustA实例,其中类型参数向上限制为X,因此它搜索GiveMeJustA[_ <: X]。
object GiveMeA {
  implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[_ <: X]) : GiveMeA[X] =
    new GiveMeA[X] { def apply() = giveMe() }
}

我们可以随后输出期望的结果。
val a = implicitly[GiveMeA[Foo]]
println(a()) // prints "Foo(foo)"

然而,一旦我们引入一个新的子类
case class FooChild(s: String) extends Foo(s)

以及相应的GiveMeJustA类型类实例

implicit object GiveMeJustAFooChild extends GiveMeJustA[FooChild] {
    def apply() = FooChild("fooChild")
}

编译器抱怨(预料中的情况)

error: could not find implicit value for parameter e: GiveMeA[Foo]
    val a = implicitly[GiveMeA[Foo]]

该解决方案适用于给定的示例。但在我的情况下,我正在使用“Generic”,而在使用“GiveMeJustA”时,该解决方案似乎不起作用,我猜测是由于宏的原因。 - tksfz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接