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]),但这是事实.
这看起来像是一个错误,但我可能会遗漏一些东西.这有意义吗?有合理的解决方法吗?
我不明白它为什么起作用,但以下代码在当前情况下成功解决了隐式问题(至少在 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)
| 归档时间: |
|
| 查看次数: |
655 次 |
| 最近记录: |