2013-05-09
■ [scala][macro] Scalaマクロについて少しだけ調べた
Scalaにも、Lispのような構文木マクロを入れようという動きがあるらしい。静的型言語の世界では構文木マクロがどのようになるのか興味があったので、少し調べてみた。
http://scalamacros.org というサイトがあるのだが、これを見ると既にScala本体にexperimental扱いで取り込まれているようだ。
概要についてはこのへんの資料が参考になった。
- Scala 2.10のマクロを試してみた - 新・たけぞう瀕死の日記
- Scala 2.10 におけるメタプログラミング: 構文木、シンボル、型について|eed3si9n
- 初めての Scala マクロ|eed3si9n
どんなものか
Scalaはコンパイル言語なので、まずマクロ本体をコンパイルしてから、マクロを使ったコードをコンパイルする、という流れになる。
それぞれのマクロは、「構文木を受け取って構文木を返す関数」として記述する。 構文木の型はc.Expr (cはContext)なのだが、Scalaは静的型付け言語なので、「どのような型の式を表す構文木か」という情報が付随している。ので、例えば文字列リテラルの構文木ならばc.Expr[String] という型になる。
Scalaはhomoiconicな言語 (構文木がプリミティブの組み合わせで表現される言語、Lisp, Prolog, Elixir, Io, Factorなど) ではないので、以下のように頑張って構文木を組み立てる必要がある。(例は上の記事から拝借)
Literal(Constant(new java.util.Date().toString))
…のだが、これは辛いので、reifyを使うことで簡単に構文木を生成できる。
reify{ "Compiled Time: " + date.eval }
reify自身もマクロとして定義されているらしい。引数で渡された構文木をそのまま値として返せばいいのか。なるほどねぇ (実際にはLispのunquote的な機能もあるのでもうちょっと複雑だけど)
マクロの利用例
https://github.com/pniederw/expecty というテストツールがある。このエラー表示、インパクトありますね…。
java.lang.AssertionError:
person.say(word1, word2) == "pong pong"
| | | | |
| | ping pong false
| ping pong
Person(Fred,42)
このpower assertionとかいう書式自体は、Spockという Groovy用テストツールに由来する。
Groovyはどうやってこれを実現してるんだろう?と思ったのだが、プラグインでパーサ自体に手を入れられるらしい。マクロどころじゃねぇw
4844330845 4798125415 4774147273