[英]Scala: Implicit evidence for class with type parameter
Here is a simple setup with two traits, a class with a covariant type parameter bounded by the previous traits, and a second class with a type parameter bounded by the other class. 这是一个带有两个特征的简单设置,一个具有由前一个特征限定的协变类型参数的类,另一个类具有由另一个类限定的类型参数。 For both classes, a particular method is available (via implicit evidence) only if one of the two traits underlies the type parameter. 对于这两个类,只有当两个特征中的一个作为类型参数的基础时,才能使用特定方法(通过隐式证据)。 This compiles fine: 编译好:
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
}
However, since Bar
is covariant in F
, I shouldn't need the F
parameter in Grill
. 但是,由于Bar
在F
是协变的,我不应该在Grill
使用F
参数。 I should just require that B
is a subtype of Bar[ReadableFoo]
. 我应该要求B
是Bar[ReadableFoo]
的子类型。 This, however, fails: 但是,这失败了:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
}
with the error: 有错误:
error: Cannot prove that Any <:< this.ReadableFoo.
def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField
Why is the implicit evidence not being taken into account? 为什么没有考虑隐含证据?
The call bar.readField
is possible because the evidence instance <:<
allows an implicit conversion from B
to Bar[ReadableFoo]
. 调用bar.readField
是可能的,因为证据实例<:<
允许从B
到Bar[ReadableFoo]
的隐式转换。
The problem I think that to call readField
you need a successive evidence parameter F <:< ReadableFoo
. 问题我认为调用readField
需要一个连续的证据参数F <:< ReadableFoo
。 So my guess is, the compiler doesn't fully substitute the type parameter of Bar
in the first search stage of the implicit resolution (because to find readField
, it just requires any Bar
in the first place). 所以我的猜测是,编译器在隐式解析的第一个搜索阶段没有完全替换Bar
的类型参数(因为要找到readField
,它首先只需要任何Bar
)。 And then it chokes on the second implicit resolution, because there is no form of 'backtracking' as far as I know. 然后它扼杀了第二个隐含的解决方案,因为据我所知,没有任何形式的“回溯”。
Anyway. 无论如何。 The good thing is, you know more than the compiler and you can engage the conversion explicitly, either by using the apply
method of <:<
, or by using the helper method implicitly
: 好处是,您比编译器更了解并且您可以通过使用<:<
的apply
方法或implicitly
使用helper方法显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
}
There is another possibility which might be the cleanest, as it doesn't rely on the implementation of <:<
which might be a problem as @Kaito suggests: 还有另一种可能是最干净的,因为它不依赖于<:<
的实现,这可能是一个问题,因为@Kaito建议:
case class Grill[+B <: Bar[_]](bar: B) {
def readField(implicit evidence: B <:< Bar[ReadableFoo]) =
(bar: Bar[ReadableFoo]).readField
}
0__'s answer (use the implicit evidence argument to turn bar
into the right type) is the answer I'd give to the specific question you asked (although I'd suggest not using implicitly
if you've got the implicit argument sitting right there). 0 __的答案(使用隐式证据参数将bar
转换为正确的类型)是我给你提出的具体问题的答案(尽管如果你有隐含的参数坐在那里我建议不要implicitly
使用) 。
It's worth noting that the situation you're describing sounds like it might be a good use case for ad-hoc polymorphism via type classes, however. 值得注意的是,您所描述的情况听起来似乎可能是通过类型类的ad-hoc多态性的一个很好的用例。 Say for example that we've got the following setup: 比如说我们有以下设置:
trait Foo
case class Bar[F <: Foo](foo: F)
case class Grill[B <: Bar[_]](bar: B)
And a type class, along with some convenience methods for creating new instances and for pimping a readField
method onto any type that has an instance in scope: 还有一个类型类,以及一些用于创建新实例和将readField
方法readField
到具有范围内实例的任何类型的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
And a couple of instances: 还有几个例子:
implicit def barInstance[F <: Foo: Readable] = Readable((_: Bar[F]).foo)
implicit def grillInstance[B <: Bar[_]: Readable] = Readable((_: Grill[B]).bar)
And finally a readable Foo
: 最后一个可读的Foo
:
case class MyFoo(x: Int) extends Foo
implicit object MyFooInstance extends Readable[MyFoo] {
def field(foo: MyFoo) = foo.x
}
This allows us to do the following, for example: 这允许我们执行以下操作,例如:
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
^
Which is what we want. 这就是我们想要的。
This is not an answer to the question, but to show that the 'type constraint' is really just an implicit conversion: 这不是问题的答案,而是表明'类型约束'实际上只是一个隐式转换:
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;
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.