2.10.0-M6でマクロを使用した実際の例を次に示します。
(更新:この例を2.10.0-M7で機能させるには、c.TypeTagをc.AbsTypeTagに置き換える必要があります。この例を2.10.0-RC1で機能させるには、c.AbsTypeTagをc.WeakTypeTagに置き換える必要があります。 )。
import scala.reflect.makro.Context
object SealednessMacros {
def exhaustive[P](ps: Seq[P]): Seq[P] = macro exhaustive_impl[P]
def exhaustive_impl[P: c.TypeTag](c: Context)(ps: c.Expr[Seq[P]]) = {
import c.universe._
val symbol = typeOf[P].typeSymbol
val seen = ps.tree match {
case Apply(_, xs) => xs.map {
case Select(_, name) => symbol.owner.typeSignature.member(name)
case _ => throw new Exception("Can't check this expression!")
}
case _ => throw new Exception("Can't check this expression!")
}
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (!internal.isSealed) throw new Exception("This isn't a sealed type.")
val descendants = internal.sealedDescendants.map(_.asInstanceOf[Symbol])
val objs = (descendants - symbol).map(
s => s.owner.typeSignature.member(s.name.toTermName)
)
if (seen.toSet == objs) ps else throw new Exception("Not exhaustive!")
}
}
これは明らかにそれほど堅牢ではなく(たとえば、階層内にオブジェクトしかないことを前提としており、失敗しますA :: B :: C :: Nil
)、それでも不快なキャストが必要ですが、迅速な概念実証として機能します。
まず、マクロを有効にしてこのファイルをコンパイルします。
scalac -language:experimental.macros SealednessMacros.scala
ここで、次のようなファイルをコンパイルしようとすると、次のようになります。
object MyADT {
sealed trait Parent
case object A extends Parent
case object B extends Parent
case object C extends Parent
}
object Test extends App {
import MyADT._
import SealednessMacros._
exhaustive[Parent](Seq(A, B, C))
exhaustive[Parent](Seq(C, A, B))
exhaustive[Parent](Seq(A, B))
}
Seq
が欠落していると、コンパイル時エラーが発生しますC
。
Test.scala:14: error: exception during macro expansion:
java.lang.Exception: Not exhaustive!
at SealednessMacros$.exhaustive_impl(SealednessMacros.scala:29)
exhaustive[Parent](Seq(A, B))
^
one error found
親を示す明示的な型パラメーターを使用してコンパイラーを支援する必要があることに注意してください。