我有一个密封的Entity
特征,可以是Root
或Descendant[P <: Entity]
。所以我需要一种方法来检查函数调用中的继承。最常见的Scala方法是要求某些类型的隐式证据(A <:< B
用于普通Scala类型,而类似A <#< B
用于实体类型)。主要问题是创建隐式函数来提供这些证据,并且在编译器试图解决它时不会混淆。
代码的基本代码段是:
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 = {}
测试数据:
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]
我尝试的解决方案之一是提供这些隐含值:
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) = // ...
但似乎编译器无法正确推断D#Parent
(它推断为匿名_$1
,而它应推断为D
的具体父类型,因此转换不会工作
另一个可能是脏的黑客是显式创建一组函数来处理具有固定深度的继承树,从而限制了函数数量的继承深度。这可能是解决方案,但很脏。
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
第三种可能的解决方案是基于宏的解决方案,但它有很多缺点,例如复杂性和对单独编译单元的要求,因此它只能被视为后备解决方案。
问题是 - 如何正确实现编译器能够解析的隐式值?