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

Tra*_*own 16 scala typeclass variance implicits subtyping

假设我有一个简单的类型类,其实例将为我提供某种类型的值:

trait GiveMeJustA[X] { def apply(): X }
Run Code Online (Sandbox Code Playgroud)

我有一些例子:

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)
}
Run Code Online (Sandbox Code Playgroud)

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

trait GiveMeA[+X] { def apply(): X }
Run Code Online (Sandbox Code Playgroud)

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

object GiveMeA {
  implicit def fromGiveMeJustA[X](implicit giveMe: GiveMeJustA[X]): GiveMeA[X] =
    new GiveMeA[X] { def apply() = giveMe() }
}
Run Code Online (Sandbox Code Playgroud)

现在我希望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]]
                        ^
Run Code Online (Sandbox Code Playgroud)

如果我们摆脱了我们无关的GiveMeJustA实例,它的工作原理是:

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

scala> implicitly[GiveMeA[Foo]]
res1: GiveMeA[Foo] = GiveMeA$$anon$1@2a4f2dcc
Run Code Online (Sandbox Code Playgroud)

尽管我们无法GiveMeA.fromGiveMeJustA将此实例应用于获取GiveMeA[Foo](或任何子类型GiveMeA[Foo]),但这是事实.

这看起来像是一个错误,但我可能会遗漏一些东西.这有意义吗?有合理的解决方法吗?

muc*_*aho 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() }
}
Run Code Online (Sandbox Code Playgroud)

然后我们可以打印出预期的输出

val a = implicitly[GiveMeA[Foo]]
println(a()) // prints "Foo(foo)"
Run Code Online (Sandbox Code Playgroud)

然而,一旦我们引入一个新的子类

case class FooChild(s: String) extends Foo(s)
Run Code Online (Sandbox Code Playgroud)

以及各自的GiveMeJustA类型类实例

implicit object GiveMeJustAFooChild extends GiveMeJustA[FooChild] {
    def apply() = FooChild("fooChild")
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨(如预期)

error: could not find implicit value for parameter e: GiveMeA[Foo]
    val a = implicitly[GiveMeA[Foo]]
Run Code Online (Sandbox Code Playgroud)