この種のコンパイル時チェックは、2.10 で利用可能になるマクロを使用するのに適しています。
Jason Zauggという名前の非常に頭の良い人が、必要なものに似たものを既に実装していますが、これは正規表現に適用されます: Regex コンパイル時間チェック。
Macrocosm を調べて、それがどのように行われるか、また同じ目的で独自のマクロをどのようにコーディングできるかを確認することをお勧めします。
https://github.com/retronym/macrocosm
本当にマクロについてもっと知りたいのであれば、まず勇気が必要です。ドキュメントは今のところ不足しており、API は変更される可能性があるからです。Jason Zaugg は 2.10-M3 で問題なく動作しますが、新しいバージョンで動作するかどうかはわかりません。
いくつかの読み物から始めたい場合:
さて、トピックに取り掛かると、Scala マクロはCATです:「コンパイル時の AST 変換」。抽象構文ツリーは、コンパイラがソース コードを表現する方法です。コンパイラは結果として生じる変換を AST に適用し、最後のステップで実際に Java バイトコードを生成します。
それでは、Jason Zaugg のコードを見てみましょう。
def regex(s: String): scala.util.matching.Regex = macro regexImpl
def regexImpl(c: Context)(s: c.Expr[String]): c.Expr[scala.util.matching.Regex] = {
import c.universe._
s.tree match {
case Literal(Constant(string: String)) =>
string.r // just to check
c.reify(s.splice.r)
}
}
ご覧のとおり、regex は、マクロ regexImpl を呼び出すことによって、文字列を受け取り、正規表現を返す特別な関数です。
マクロ関数は、最初のパラメーター リストでコンテキストを受け取り、2 番目の引数リストでマクロのパラメーターを c.Expr[A] の形式で受け取り、c.Expr[B] を返します。c.Expr はパスに依存する型であることに注意してください。つまり、Context 内で定義されたクラスであるため、2 つのコンテキストがある場合、次は無効になります。
val c1: context1.Expr[String] = ...
val c2: context2.Expr[String] = ...
val c3: context1.Expr[String] = context2.Expr[String] // illegal , compile error
コードで何が起こっているかを見ると、次のようになります。
- s.tree に一致する一致ブロックがあります
- s.tree が定数 String を含むリテラルの場合、 string.r が呼び出されます
ここで何が起こっているかというと、文字列から Predef.scala で定義された StringOps への暗黙的な変換があり、これはすべての scala ソースのコンパイルで自動的にインポートされます。
implicit def augmentString(x: String): StringOps = new StringOps(x)
StringOps は scala.collection.immutable.StringLike を拡張します。これには以下が含まれます。
def r: Regex = new Regex(toString)
マクロはコンパイル時に実行されるため、これはコンパイル時に実行され、例外がスローされるとコンパイルは失敗します (これは、無効な正規表現文字列から正規表現を作成する動作です)。
注: 残念ながら、API は非常に不安定です。http://scamacros.org/documentation/reference.html を見ると 、 Context.scala への壊れたリンクが表示されます。正しいリンクは https://github.com/scala/scala/blob/2.10.x/src/reflect/scala/reflect/makro/Context.scalaです