Scala:在编译时验证 class 参数不是 instanceOf 特征

Scala: verify class parameter is not instanceOf a trait at compile time

在编译时我想验证 class 参数不是特定特征 T 的实例。我知道如何在运行时使用 requirecase match 但想知道如何在编译时完成此操作以防止用户提供特定类型的对象混合。

我研究过 scala macros/reflection 但无法完全理解它。

trait A
trait B
trait T
abstract class C extends A with B

case class P(c: C){
  require(!c.isInstanceOf[T]) // how to do this at compile time ?
}

// usage as below
object c1 extends C
object c2 extends C
object c3 extends C
object c4 extends C with T

val l = List(c1, c2, c3, c4).map(k => P(k)) // should fail at compile time

您无法使用 List 执行此操作。 List(c1, c2, c3, c4) 中的所有元素都属于同一类型,即 C,并且其中之一的类型为 C with T 的信息将丢失。

new C {}new C with T {}c1, c2, c3, c4 的运行时值,编译器在编译 List(c1, c2, c3, c4).

时无法访问它们

您可以使用 HList 执行此操作。使用 shapeless.<:!<shapeless.ops.hlist.LiftAll 和 kind-projector

def noElementIsSubtypeOfT[L <: HList](l: L)(implicit liftAll: LiftAll[* <:!< T, L]) = null

noElementIsSubtypeOfT(c1 :: c2 :: c3 :: HNil) // compiles
// noElementIsSubtypeOfT(c1 :: c2 :: c3 :: c4 :: HNil) // doesn't compile

def noElementIsSubtypeOfT[L <: HList : LiftAll[* <:!< T, *]](l: L) = null

对于class参数你可以做

case class P[U <: C](c: U)(implicit ev: U <:!< T)

P(c1) // compiles
P(c2) // compiles
P(c3) // compiles
// P(c4) // doesn't compile

case class P[U <: C : * <:!< T](c: U)