简体   繁体   English

Scala:类型参数类的隐式证据

[英]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 . 但是,由于BarF是协变的,我不应该在Grill使用F参数。 I should just require that B is a subtype of Bar[ReadableFoo] . 我应该要求BBar[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是可能的,因为证据实例<:<允许从BBar[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.

相关问题 Scala编译错误-找不到类型为证据的隐式值 - Scala compile error - could not find implicit value for evidence parameter of type 为Scala中的类型别名提供隐式证据 - Provide implicit evidence for a type alias in Scala 无法找到类型证据参数的隐式值 - Could not find implicit value for evidence parameter of type 找不到类型^的证据参数的隐含值 - could not find implicit value for evidence parameter of type ^ 斯卡拉<console> :24: 错误: 找不到微风.storage.DefaultArrayValue [Any] 类型的证据参数的隐式值 - Scala <console>:24: error: could not find implicit value for evidence parameter of type breeze.storage.DefaultArrayValue[Any] Scala / specs2:找不到 AsExecution[ExecutionEnv =&gt; MatchResult[Future[AuthenticationResult]]] 类型的证据参数的隐式值 - Scala / specs2 : could not find implicit value for evidence parameter of type AsExecution[ExecutionEnv => MatchResult[Future[AuthenticationResult]]] 找不到scala.reflect.ClassManifest [T]类型的证据参数的隐式值 - Could not find implicit value for evidence parameter of type scala.reflect.ClassManifest[T] 如何为Scala隐式类使用类型参数? - How to use type parameter for Scala implicit class? Scala隐式类嵌套类型参数 - Scala implicit class nested type parameter 加特林scala扩展失败,找不到证据参数的隐式值 - Gatling scala extension fails with, could not find implicit value for evidence parameter
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM