6

私は日付 (20120522) の scala 2.10.0-snapshot を使用しており、次の Scala ファイルがあります。

これは型クラスと基本的な型クラスのインスタンスを定義します:

package com.netgents.typeclass.hole

case class Rabbit

trait Hole[A] {
  def findHole(x: A): String
}

object Hole {
  def apply[A: Hole] = implicitly[Hole[A]]
  implicit val rabbitHoleInHole = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole companion object"
  }
}

これはパッケージ オブジェクトです。

package com.netgents.typeclass

package object hole {

  def findHole[A: Hole](x: A) = Hole[A].findHole(x)

  implicit val rabbitHoleInHolePackage = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in Hole package object"
  }
}

そしてここにテストがあります:

package com.netgents.typeclass.hole

object Test extends App {

  implicit val rabbitHoleInOuterTest = new Hole[Rabbit] {
    def findHole(x: Rabbit) = "Rabbit found the hole in outer Test object"
  }

  {
    implicit val rabbitHoleInInnerTest = new Hole[Rabbit] {
      def findHole(x: Rabbit) = "Rabbit found the hole in inner Test object"
    }

    println(findHole(Rabbit()))
  }
}

ご覧のとおり、は検索しようとしてHoleいるメソッドを定義する単純な型クラスです。Rabbit私はそれに関する暗黙の解決規則を理解しようとしています。

  • 4 つの型クラス インスタンスすべてのコメントを外すと、scalac はrabbitHoleInHolePackageとのあいまいさについて文句を言いますrabbitHoleInHole。(どうして?)

  • をコメントアウトするrabbitHoleInHoleと、scalac がコンパイルされ、"Rabbit が Hole パッケージ オブジェクトに穴を見つけました" というメッセージが返されます。(ローカルスコープの暗黙を優先すべきではありませんか?)

  • 次にコメントアウトするrabbitHoleInHolePackageと、scalac はrabbitHoleInOuterTestとのあいまいさについて文句を言いrabbitHoleInInnerTestます。(なぜですか? eed3si9n の記事で、以下にリストされている URL で、彼は暗黙のうちに内側と外側のスコープが異なる優先順位を取ることができることを発見しました。)

  • 次に をコメントアウトするrabbitHoleInInnerTestと、scalac がコンパイルされ、「Rabbit が外側の Test オブジェクトに穴を見つけました」というメッセージが返されます。

ご覧のとおり、上記の動作は、暗黙の解決について読んだルールにはまったく従っていません。インスタンスのコメント化/コメント化解除で実行できる組み合わせの一部のみを説明しましたが、それらのほとんどは実際には非常に奇妙です-インポートとサブクラスにはまだ触れていません.

suereth によるプレゼンテーション、sobralよる stackoverflow の回答、およびeed3si9nによる非常に精巧な再訪を読んで見ましたが、まだ完全に困惑しています。

4

1 に答える 1

4

パッケージ オブジェクトの暗黙的要素と型クラスのコンパニオンを無効にすることから始めましょう。

package rabbit {
  trait TC

  object Test  extends App {
    implicit object testInstance1 extends TC { override def toString = "test1" }

    {
      implicit object testInstance2 extends TC { override def toString = "test2" }

      println(implicitly[TC])
    }
  }
}

Scalac は、スコープ内の暗黙的なもの、 find 、testInstance1およびを探しtestInstance2ます。1 つがより狭いスコープにあるという事実は、それらが同じ名前を持つ場合にのみ関係があります。シャドウイングの通常のルールが適用されます。個別の名前を選択しましたが、どちらの暗黙的も他のものより具体的ではないため、あいまいさが正しく報告されます。

別の例を試してみましょう。今回は、ローカル スコープの暗黙的なスコープと、パッケージ オブジェクトの暗黙的なスコープを組み合わせます。

package rabbit {
  object `package` {
    implicit object packageInstance extends TC { override def toString = "package" }
  }

  trait TC

  object Test  extends App {
    {
      implicit object testInstance2 extends TC { override def toString = "test2" }

      println(implicitly[TC])
    }
  }
}

そこで何が起こるの?暗黙的な検索の最初のフェーズでは、以前と同様に、呼び出しサイトの範囲内のすべての暗黙的なものを考慮します。この場合、 と がtestInstance2ありpackageInstanceます。これらはあいまいですが、そのエラーを報告する前に、第 2 フェーズが開始され、 の暗黙のスコープが検索されますTC

しかし、ここでの暗黙のスコープには何が含まれているのでしょうか? TCコンパニオン オブジェクトすらありませんか? ここで、Scala リファレンスの 7.2 で正確な定義を確認する必要があります。

型 T の暗黙のスコープは、暗黙のパラメーターの型に関連付けられているクラスのすべてのコンパニオン モジュール (§5.4) で構成されます。ここで、クラス C が T の一部の基本クラス (§5.1.2) である場合、クラス C は型 T に関連付けられていると言います。

型の部分は次のTとおりです。

  • T複合型T1 with ... with Tn、 の部分の結合、T1, ..., TnおよびTそれ自体である場合、
  • Tがパラメータ化された型である場合、と のS[T1, ..., Tn]部分の和集合ST1,...,Tn
  • Tがシングルトン型の場合p.type、 の型の部分p
  • T型射影の場合、それ自体だけでなくS#U、の部分、ST
  • それ以外の場合は、Tそれ自体だけです。

を探していrabbit.TCます。型システムの観点からは、これは の省略形です。rabbit.type#TCここで、rabbit.typeは通常のオブジェクトであるかのように、パッケージを表す型です。ルール 4 を呼び出すと、パーツTCおよびが得られp.typeます。

それで、それはどういう意味ですか?簡単に言うと、パッケージ オブジェクトの暗黙的なメンバーも暗黙的なスコープの一部です。

上記の例では、これにより、暗黙的な検索の第 2 段階で明確な選択肢が得られます。

他の例も同じように説明できます。

要約すれば:

  • 暗黙の検索は 2 つのフェーズで進行します。インポートとシャドウイングの通常のルールにより、候補のリストが決定されます。
  • ネストされたパッケージを使用していると仮定すると、外側のパッケージ オブジェクトの暗黙的なメンバーもスコープ内にある可能性があります。
  • 候補が複数ある場合は、静的オーバーロードのルールを使用して、勝者があるかどうかを確認します。追加のタイブレーカーとして、コンパイラーは、最初のスーパークラスで定義された別のものよりも 1 つの暗黙的なものを優先します。
  • 最初のフェーズが失敗した場合、暗黙のスコープがほぼ同じ方法で参照されます。(違いは、異なるコンパニオンからの暗黙のメンバーが、互いにシャドーイングせずに同じ名前を持つことができることです。)
  • 囲んでいるパッケージからのパッケージ オブジェクトの暗黙も、この暗黙のスコープの一部です。

アップデート

Scala 2.9.2 では、動作が異なり、間違っています。

package rabbit {
  trait TC

  object Test extends App {
    implicit object testInstance1 extends TC { override def toString = "test1" }

    {
      implicit object testInstance2 extends TC { override def toString = "test2" }

      // wrongly considered non-ambiguous in 2.9.2. The sub-class rule
      // incorrectly considers:
      //
      // isProperSubClassOrObject(value <local Test>, object Test)
      //   isProperSubClassOrObject(value <local Test>, {object Test}.linkedClassOfClass)
      //   isProperSubClassOrObject(value <local Test>, <none>)
      //     (value <local Test>) isSubClass <none>
      //        <notype> baseTypeIndex <none> >= 0
      //        0 >= 0
      //        true
      //     true
      //   true
      // true
      //
      // 2.10.x correctly reports the ambiguity, since the fix for
      //
      // https://issues.scala-lang.org/browse/SI-5354?focusedCommentId=57914#comment-57914
      // https://github.com/scala/scala/commit/6975b4888d
      //
      println(implicitly[TC])
    }
  }
}
于 2012-05-26T19:16:00.490 に答える