Scala: убедитесь, что параметр класса не является экземпляром признака во время компиляции - PullRequest
1 голос
/ 12 апреля 2020

Во время компиляции я хочу убедиться, что параметр класса НЕ является экземпляром определенной черты T. Я знаю, как это сделать во время выполнения, используя require или case 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

1 Ответ

2 голосов
/ 13 апреля 2020

Вы не можете сделать это с 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)
...