这是一个简单的设置,有两个特征,一个具有由前一个特征限定的协变类型参数的类,另一个类具有由另一个类限定的类型参数。对于这两个类,只有当两个特征中的一个作为类型参数的基础时,才能使用特定方法(通过隐式证据)。编译好了:
trait Foo
trait ReadableFoo extends Foo {def field: Int}
case class Bar[+F <: Foo](foo: F) {
def readField(implicit evidence: F <:< ReadableFoo) = foo.field
}
case class Grill[+F <: Foo, +B <: Bar[F]](bar: B) {
def readField(implicit evidence: F <:< ReadableFoo) = bar.readField
}
但是,由于Bar
中的F
是协变的,我不应该在F
中使用Grill
参数。我应该要求B
是Bar[ReadableFoo]
的子类型。然而,这失败了:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
}
错误:
error: Cannot prove that Any <:< this.ReadableFoo.
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
为什么没有考虑隐含证据?
答案 0 :(得分:6)
可以调用bar.readField
,因为证据实例<:<
允许从B
隐式转换为Bar[ReadableFoo]
。
问题我认为要调用readField
你需要一个连续的证据参数F <:< ReadableFoo
。所以我的猜测是,编译器在隐式解析的第一个搜索阶段没有完全替换Bar
的类型参数(因为找到readField
,它只需要任何Bar
in第一名)。然后它在第二个隐含的解决方案上窒息,因为据我所知,没有任何形式的“回溯”。
反正。好消息是,您知道的不仅仅是编译器,您可以通过使用apply
的{{1}}方法或使用辅助方法<:<
明确地参与转换:
implicitly
还有另一种可能是最干净的,因为它不依赖case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = evidence(bar).readField
}
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) =
implicitly[Bar[ReadableFoo]](bar).readField
}
的实施,这可能是一个问题,因为@Kaito建议:
<:<
答案 1 :(得分:6)
0 __的答案(使用隐式证据参数将bar
转换为正确的类型)是我给你提出的具体问题的答案(尽管我建议不要使用implicitly
你有隐含的论据坐在那里。)
值得注意的是,您所描述的情况听起来似乎是通过类型类的ad-hoc多态性的一个很好的用例。比如说我们有以下设置:
trait Foo
case class Bar[F <: Foo](foo: F)
case class Grill[B <: Bar[_]](bar: B)
一个类型类,以及一些方便的方法,用于创建新实例和将readField
方法移植到任何具有范围实例的类型上:
trait Readable[A] { def field(a: A): Int }
object Readable {
def apply[A, B: Readable](f: A => B) = new Readable[A] {
def field(a: A) = implicitly[Readable[B]].field(f(a))
}
implicit def enrich[A: Readable](a: A) = new {
def readField = implicitly[Readable[A]].field(a)
}
}
import Readable.enrich
还有几个例子:
implicit def barInstance[F <: Foo: Readable] = Readable((_: Bar[F]).foo)
implicit def grillInstance[B <: Bar[_]: Readable] = Readable((_: Grill[B]).bar)
最后是可读的Foo
:
case class MyFoo(x: Int) extends Foo
implicit object MyFooInstance extends Readable[MyFoo] {
def field(foo: MyFoo) = foo.x
}
这允许我们执行以下操作,例如:
scala> val readableGrill = Grill(Bar(MyFoo(11)))
readableGrill: Grill[Bar[MyFoo]] = Grill(Bar(MyFoo(11)))
scala> val anyOldGrill = Grill(Bar(new Foo {}))
anyOldGrill: Grill[Bar[java.lang.Object with Foo]] = Grill(Bar($anon$1@483457f1))
scala> readableGrill.readField
res0: Int = 11
scala> anyOldGrill.readField
<console>:22: error: could not find implicit value for evidence parameter of
type Readable[Grill[Bar[java.lang.Object with Foo]]]
anyOldGrill.readField
^
这就是我们想要的。
答案 2 :(得分:1)
这不是问题的答案,而是表明“类型约束”实际上只是一种隐式转换:
Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_33).
Type in expressions to have them evaluated.
Type :help for more information.
scala> trait A { def test() {} }
defined trait A
scala> class WhatHappens[T] { def test(t: T)(implicit ev: T <:< A) = t.test() }
defined class WhatHappens
scala> :javap -v WhatHappens
...
public void test(java.lang.Object, scala.Predef$$less$colon$less);
Code:
Stack=2, Locals=3, Args_size=3
0: aload_2
1: aload_1
2: invokeinterface #12, 2; //InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object;
7: checkcast #14; //class A
10: invokeinterface #17, 1; //InterfaceMethod A.test:()V
15: return
...
LocalVariableTable:
Start Length Slot Name Signature
0 16 0 this LWhatHappens;
0 16 1 t Ljava/lang/Object;
0 16 2 ev Lscala/Predef$$less$colon$less;
...