The code fragment provided is a made-up minimalistic example just to demonstrate the issue, not related to actual business logic types.
In the code below we have a nested Entry
type inside Registry
type.
class Registry[T](name: String) {
case class Entry(id: Long, value: T)
}
That makes sense cause Entries of different Registries are kind of different, incomparable types.
Then we may have an implicit Ops class, for example, used in tests, which binds our registries to some test storage implementation, a simple mutable map
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
}
}
The problem is: the Entry
consructed inside Ops wrapper is treated as an incomparable type to the original Registry object
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)
}
The question is: Is there a way to declare Ops signatures to make compiler understand that we're working with same inner type? (
I've also tried self.type#Entry
in RegistryOps
with no luck)
If I miss some understanding and they are actually different types, I would appreciate any explanations and examples why considering them as same may break type system. Thanks!