Rubyのアノテーション
http://martinfowler.com/bliki/RubyAnnotations.html
Rubyで人気のある機能のひとつにメタプログラミングのサポートがある。 メタプログラミングとは言語自身を変えてしまうように動作する機能のことだ。 たとえば、新しい予約語を導入するみたいな、ね。
主流の「中かっこ」言語族はメタプログラミングをほとんどサポートしてないが、メタプログラミングの有用な応用として「注記」をあげることができる。 「注記」は内部DSLスタイルで言語を拡張するにあたって重要な役割を果たす。 一見すると、Rubyは注記をサポートしていないように見えるが、頭をひねってみればそれが見えてくるはずだ。
注記を使って、言語要素(クラス、メソッド、フィールド等)をマーカーでタグ付けする。 とりわけ、その要素のメタデータをね。 そうすると、そのメタデータを実行時ないしコンパイル時に利用することができる。
こいつの良い例は、NUnit2で開拓されたテストマーキングだ。
[Test] public void SomeTestMethod() {...
実行するとNUnitフレームワークはクラスを見て、[Test]と注記されているメソッドを探してそれを実行する。
注記はパラメータをとることもできる。 Pascalのサブレンジを使いたくて焦れているとしよう。 これを使うと変数の有効範囲を示すことができる。 こんな風に定義することになる。
@ValidRange(lower = 1, upper = 1000)
private int weight; // in lb
@ValidRange(lower = 1, upper = 120)
private int height; // in inches
これはPascalのサブレンジの概念とまったく同じではなく、ここでは単に有効範囲を定義しているに過ぎないので、値をチェックするためのメカニズムを作らないわけにはいかない。 たとえばそのオブジェクトのisValidメソッドを呼び出すような、ね(こいつがContextualValidationに満たないことは同意するよ)。 しかし、ここでの鍵は、その変数について独自の宣言的な記述を定義できたことそのものにあるのだ。
で、どうやってこれをRubyでやろう?こんな感じだろうか。
validate_range :@height, :with => 1..120
validate_range :@weight, :with => 1..1000
文法上、Java(や.NET)との大きな違いは、Javaでは注記したい要素の直前に注記を置かなければならないということだ。 Rubyでは要素の名前を使って注記することができる。 これのせいで少し打鍵する回数が増えるが(Rubyでは珍しいことだ)、その代わりに注記をクラス中の好きなところに置く自由が与えられる。 また複数の言語要素を参照する注記を書くのも楽になる。
2つのスタイルの間のより深いレベルの違いは、その実装のされ方にある。 中かっこ族の注記は特別な言語構築であり、言語要素にメタオブジェクトとして特殊なオブジェクトをアタッチすることになる。 こうした注記オブジェクトはコンパイル時(Javaのaptで)にも、実行時にも問い合わせることもプロセスすることもできる。
Rubyの注記は、それに対して、通常はスーパークラスかインクルードされるモジュールで定義されるクラスメソッドである。 クラス定義中に直接それを書き、クラスがロードされたときに実行されるようにする。 結果として、それらは特定の言語機能ではなく、クラスメソッドの使い方の一種ということになる。 メタデータオブジェクトを作る必要がなく(ただしそうしたければそうすることもできる)、オブジェクトにはそれに負わせたいタスクだけを負わせることができるのだ。
Rubyの動的な性質のため、注記でもっと面白いこともできる。
コード生成はその顕著な例だ。
もっとも目立つ例としてattr_accessor :height
のようなもので、これはそのフィールドへのゲットメソッドとセットメソッドを生成してくれるわけで、要するにクラス自身を修正してくれる。
aptを使えばJavaでも同じようなことができるが、クラスそのものを修正することはできない。
ビルドスクリプトとパーシャルクラスでこれと同じようなことがC#でできるが、この種の実行時コード生成はRubyではより自然・天然なもので、RubyのLisp的要素のひとつとなっている。
Ruby信者たちはこれを注記とは呼ばない。 私がこう呼ぶのは、言語を横断した一般的テクニックを探すのが好きだからだ。 私にとってこれは一般的なテクニックであり、「注記」はそれを示すための良い一般的な単語に思える。 Ruby信者たちが同意してくれるかどうかわからないけれど。