10

これは、2つの特性を持つ単純なセットアップです。前の特性によって制限された共変型パラメーターを持つクラスと、他のクラスによって制限された型パラメーターを持つ2番目のクラスです。どちらのクラスでも、2つの特性のいずれかがtypeパラメーターの基礎となる場合にのみ、特定のメソッドを(暗黙の証拠を介して)使用できます。これはうまくコンパイルされます:

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は必要ありません。のサブタイプである必要があります。ただし、これは失敗します。FGrillBBar[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

暗黙の証拠が考慮されていないのはなぜですか?

4

3 に答える 3

7

0__の答え(暗黙の証拠引数を使用barして正しいタイプに変換する)は、あなたが尋ねた特定の質問に対して私が与える答えです(ただしimplicitly、暗黙の引数がすぐそこにある場合は使用しないことをお勧めします)。

ただし、説明している状況は、型クラスを介したアドホック多相の良いユースケースのように聞こえることは注目に値します。たとえば、次の設定があるとします。

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
              ^

それが私たちが望んでいることです。

于 2012-07-01T23:39:48.303 に答える
6

bar.readFieldエビデンスインスタンスがからへ<:<の暗黙の変換を許可するため、呼び出しが可能です。BBar[ReadableFoo]

readField問題を呼び出すには、連続した証拠パラメータが必要だと思いますF <:< ReadableFooBarだから私の推測では、コンパイラは暗黙の解決の最初の検索段階での型パラメータを完全に置き換えません(見つけるために、最初にreadField何かが必要なだけBarです)。そして、私が知る限り、「バックトラック」の形式がないため、2番目の暗黙の解決で窒息します。

ともかく。良いことは、コンパイラー以上のことを知っていて、のapplyメソッドを使用するか<:<、ヘルパーメソッドを使用して、明示的に変換を実行できること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が示唆しているように、実装が問題になる可能性があることに依存していないため、最もクリーンな可能性があります。

case class Grill[+B <: Bar[_]](bar: B) {
  def readField(implicit evidence: B <:< Bar[ReadableFoo]) =
     (bar: Bar[ReadableFoo]).readField
}
于 2012-07-01T20:23:42.073 に答える
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;
...
于 2012-07-02T00:09:41.350 に答える