5

提供されているコード フラグメントは、問題を示すためだけに作成された最小限の例であり、実際のビジネス ロジックの種類とは関係ありません。

以下のコードでは、Entry型の中にネストされた型がありRegistryます。

class Registry[T](name: String) {
  case class Entry(id: Long, value: T)
}

それは理にかなっています。異なるレジストリのエントリは、一種の異なる、比較できないタイプです。

次に、たとえば、テストで使用される暗黙的な Ops クラスを使用する場合があります。これは、レジストリをテスト ストレージの実装、単純な変更可能なマップにバインドします。

object testOps {

  import scala.collection.mutable

  type TestStorage[T] = mutable.Map[Long, T]

  implicit class RegistryOps[T](val self: Registry[T])(
    implicit storage: TestStorage[T]
  ) {

    def getById(id: Long): Option[self.Entry] = 
      storage.get(id).map(self.Entry(id, _))

    def remove(entry: self.Entry): Unit       = storage - entry.id
  }
}

問題はEntryOps ラッパー内で構築されたものは、元のレジストリ オブジェクトと比較できない型として扱われることです。

object problem {

  case class Item(name: String)

  val items = new Registry[Item]("elems")

  import testOps._

  implicit val storage: TestStorage[Item] = 
    scala.collection.mutable.Map[Long, Item](
      1L -> Item("whatever")
    )
  /** Compilation error: 
   found   : _1.self.Entry where val _1: testOps.RegistryOps[problem.Item]
   required: eta$0$1.self.Entry
  */
  items.getById(1).foreach(items.remove)
}

問題は、Ops シグネチャを宣言して、同じ内部型で作業していることをコンパイラに理解させる方法はありますか? (私も試してみself.type#EntryましRegistryOpsたが運が悪かったです)理解が欠けていて、実際には異なる型である場合、型システムが壊れる可能性があると見なす理由と例を教えていただければ幸いです。ありがとう!

4

2 に答える 2