Scala:在编译时验证类参数不是特征的示例

yk9xbfzb  于 2022-11-09  发布在  Scala
关注(0)|答案(1)|浏览(183)

在编译时,我想验证类参数不是特定特征T的示例。我知道如何在运行时使用requirecase match来做这件事,但想知道如何在编译时做到这一点,以防止用户提供特定类型的对象混合。
我研究过Scala宏/反射,但不能完全理解它。

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
pwuypxnk

pwuypxnk1#

使用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投影仪

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

对于类参数,您可以这样做

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)

实际上,有一种方法可以用List修复代码。您可以将类P设置为隐式(因此您定义了隐式转换)并指定列表List[P[_]](...)的类型(所谓的磁体模式12345 6 7P是磁体)

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

List[P[_]](c1, c2, c3) // compiles

List[P[_]](c1, c2, c3, c4) // doesn't compile, type mismatch: found: c4.type, required: P[_]

相关问题