出版社より献本御礼。

「ただの」プログラマーで終わりたくない人、必携。

Rubyistsはもとより、そうでない人も。

むしろRuby以外のプログラミング言語をホームグラウンドにしている人は、Rubyistsよりもさらに得るところが大きいかもしれない。「私の言語ではどうやる?」を考え、実際にやってみることで、Rubyも「母国語」も理解が深まるのだから。

本書「メタプログラミングRuby」は、Rubyにおけるメタプログラミングを物語形式で学ぶ一冊。舞台はRubyを使っているある会社。平凡なプログラマー、ボブは、いかにしてメタプログラマーへと一週間で成長したか。

目次
序文
謝辞
はじめに
第一部 メタプログラミングRuby
第1章 月曜日:オブジェクトモデル
第2章 火曜日:メソッド
第3章 水曜日:ブロック
第4章 木曜日:クラス定義
第5章 金曜日:コードを記述するコード
第6章 エピローグ
第二部 Railsメタプログラミング
第7章 ActiveRecordの設計
第8章 ActiveRecordの中身
第9章 安全なメタプログラミング
付録
付録A よく使うイディオム
ドメイン特化言語
魔術書
参考書
から騒ぎ

メタプログラミング(metaprogramming)とは、何か。

プログラムを書くのがプログラミングなら、プログラムを書くプログラムを書くのがメタプログラミングということになる。なんだかすごそうで、すごくこわそうな話であるが、実はそれほど特別な話ではない。また、動的言語に限った話でもない。Makefileを編集するというのはある意味メタプログラミングだし、ひな形をコピペするのだって、それをエディタやIDEのマクロでやるのであればある意味立派なメタプログラミングである。

しかし、メタプログラミングがどれくらい容易で、そしてどれくらい用いられているかは言語によって違う。コンパイルを前提とする静的言語よりも動的言語の方が容易だし、そして同じ動的言語でも、人が書いたメタプログラムをライブラリなどの形で利用することが多い言語もあれば、現場でも遠慮なくどしどし使う言語もある。Rubyは後者の最右翼ではなかろうか?

例えばオープンクラス。 JavaScript も Perl もクラスはオープン、つまり実行中にも動的に変更可能なのであるが、組み込みのクラスに現場で手を加えるのには抵抗がある。rubyismがいかんなく発揮されたprototype.jsに対する批判はほとんどそれに集約されるぐらいだし、魔術は独り占めするのではなくモジュールにしてCPANにうpすることが文化として根付いている Perl ですら、UNIVERSAL::ネームスペースを改変するものには厳しい目が向けられる。

ところがRubyの文化においては、こういうコードを書くのは普通のようである。

class String
  def camelize
    gsub (/_(\w)/) { $1.upcase } 
  end
end

puts 'get_element_by_id'.camelize

いわゆるモンキーパッチだ。

これを組み込みクラスの組み替えに慣れているはずの rubyists たちですら呆れるほどやったのが Ruby on Rails である。本書では第二部でこれを扱っている。いかに Rails がそれをやっているのか。そして自らがやりすぎるのを抑えているのか。

そう。やりすぎ。プログラミングが技術なら、メタプログラミングは魔術。覚えたの頃はつい使いすぎてしまう。しかし魔術だけあって、使いすぎれば自らを滅ぼす。しかも往々にして周りまで巻き添えにして。

def Object.const_missing(k)
  42
end

p LIFE
p UNIVERSE
p EVERYTHING

これに対するMatzの答えが素敵だ。

Rubyは君を信頼する。Rubyは君を分別のあるプログラマとして扱う。Rubyはメタプログラミングのような強力な力を与える。ただし、大いなる力には、大いなる責任が伴うことを忘れてはならない。

大人の世界へ、ようこそ。

Dan the Yet Another Metaprogrammer

追記:ちなみに lexical scope と dynamic scope の双方をサポートしているPerlでは、以下のようにしてモンキーパッチを局所化できる。

use v5.14; # strict, warnings, and say
{
  package Bad;
  sub new { bless {}, shift };
  sub broken{ "I'm broken" }
}
my $o = Bad->new;
say $o->broken;
{
    no warnings 'redefine';
    local *Bad::broken = sub { "I'm fine" };
    say $o->broken;
}
say $o->broken;

Rubyではどうやるのだろう。まあPerlでも滅多にこんなことやらないのだけど。

追^2記:内容が大人向けな分、翻訳が大人げないのもいい。「頭文字M」「絶望した!」「ジャッジメントですの」…