I have a sealed Entity
trait that can be either Root
or Descendant[P <: Entity]
. So I need a way to check that inheritance in function invocations. The most common Scala way is to require an implicit evidence of some type (A <:< B
for plain Scala types, and something like A <#< B
for entity ones). The main problem is to create implicit functions that will provide these evidences and will not confuse the compiler when it will try to resolve it.
Base snippet of code is:
sealed trait Entity
class Root extends Entity
class Descendant[P <: Entity] extends Entity
sealed trait <#<[From, To]
def check[F <: Entity, T <: Entity](implicit ev: F <#< T): Unit = {}
And test data for it:
object TRoot extends Root
object TBase extends Descendant[TRoot.type]
object TSub extends Descendant[TBase.type]
check[TRoot.type, TRoot.type]
check[TBase.type, TRoot.type]
check[TSub.type, TRoot.type]
One of solutions that I tried was to provide these implicit values:
trait Descendant[P <: Entity] { type Parent = P }
// In some parent object, so it will have lower priority
def childConforms[T <: Entity, D <: Descendant[_, _]](implicit ev: D#Parent <#< T):
(D <#< T) = // create instance, e.g. from singleton
// In child object with higher priority than `childConforms`
def selfConforms[T <: Entity): (T <#< T) = // ...
But it seems that compiler is not able to correctly infer D#Parent
(it infers as anonymous _$1
, while it should infer as concrete parent type of D
), so conversion does not work.
Another possibly dirty hack is to explicitly create a set of functions that will handle inheritance trees with fixed depths, thus limiting inheritance depth with amount of functions. This is possibly solution, but quite dirty.
def selfConforms[T <: Entity]: (T <#< T) = ...
def child1Conforms[T <: Entity, D <: Descendant[T]]: (D <#< T) = ...
def child2Conforms[T <: Entity, D <: Descendant[Descendant[T]]]: (D <#< T) = ...
// and so on
The third possible solution is a macro-based one, but it has a significant amount of drawbacks, such as complexity and requirement for separate compilation unit, so it can be considered only as fallback one.
The question is — how to properly implement implicit values which compiler will be able to resolve?