数々の“スペル”で高度なプログラミング

Rubyの魔術

2010/09/13

 元JavaプログラマのPaolo Perrotta氏は、Rubyを使い始めた頃のことを振り返り、こう話す。

 「私はJavaの世界からRubyに来た当初、とてもハッピーなプログラマでした。Rubyっていいね、かっこいいじゃん! と。ところが、最先端のイケてるライブラリの中を覗いてみたら、分からないところだらけだったんです……」。

 RubyKaigi2010で「A Metaprogramming Spell Book」(あるメタプログラミング魔術の書)と題した講演を行ったイタリア人プログラマのPerrotta氏は、Ruby on Railsのソースコードを覗いてみたときの驚きをこう表現する。

spell01.jpg 魔術書を開いたような印象的な絵で講演を始めるPaolo Perrotta氏

 「例えばalias_method_chainというメソッドは、一体どこにあるんだと探しました。どこにあるか分からないんです。こんなことJavaではあり得ません。結局、テキスト検索で発見したコードがこれですが……、このリストにあるうち、私に分からなかったのは、ここと、ここと、ここです」

 Perrotta氏がこう言うと、画面に表示されたコードが次々と黄色や赤にハイライトされ、最終的には全部の行がハイライトされてしまい、聴衆は思わず笑い出す。Perrotta氏にとって、Railsのコードは最初のうち、全然読めるようなものではなかったというのだ。「表面下で自分に分からないことがいっぱい起こっているに違いないと思いました」というPerrotta氏は、Javaと同じようにしか、Rubyを使っていなかったのだという。

highlight.jpg Perrotta氏が「分からなかった個所」といってハイライトすると、コードのほとんど全部が赤や黄色に

 Perrotta氏が「表面下」で起こっていると指摘するのは、“メタプログラミング”と呼ばれる技法のことだ。メタプログラミングは「プログラムをプログラムすること」などと言われ、魔術めいたところがある。そうしたRuby/Railsで使われるメタプログラミングの技法を、Perrotta氏は「スペル」(spell:呪文、魔術)と呼ぶ。スペルは、デザインパターンの“パターン”や、プログラミング言語ごとにある決まり文句、“イディオム”と同類の用語だ。

book01.jpg 古書を紐解くように、1つ1つテクニックを概観するPerrotta氏

“スペル”の数々

 Perrotta氏はスペルを分類・整理してRubyの内部構造とともに説明した書籍を2010年2月に出版している。これは最近『メタプログラミングRuby』(Paolo Perrotta著・角征典訳、アスキー・メディアワークス)として邦訳が出たばかりでもある。

 『メタプログラミングRuby』には、謎めいたスペルが並ぶ。

  • オープンクラス
  • アラウンド・エイリアス
  • ブランク・スレート(何も書かれていない白板の意味)
  • クラス・マクロ
  • クリーンルーム
  • コンテキスト探査機
  • 動的ディスパッチ
  • 動的メソッド
  • ゴーストメソッド
  • 動的プロキシ
  • フラットスコープ

 比較的よく知られているRubyのメタプログラミングの技法はmethod_missingという特殊なメソッドを使う「ゴーストメソッド」だろう。method_missingは、Ruby処理系がオブジェクトモデルの階層を上がってメソッドを探索した結果、継承しているクラス・モジュールのどこにも定義が見つからなかったときに呼び出されるメソッドだ。これを使うと、ソースコード中に存在しない(静的な定義がない)、ゴースト(お化け)のようなメソッドを使うことができる。

 ゴーストメソッドの実例は、例えばORMライブラリにある。データベースのカラム名を含むメソッドは事前(ライブラリ設計時)には定義しようがないが、RailsのActiveRecordでは「find_by_username」というように、存在しないはずのメソッドを使うことができる。RailsではURLディスパッチ処理についても、任意の名前をメソッドとして呼ぶことでルーティングを定義するようなことができる。背後では、ルーティングを定義するとともにビューの中から使える関連メソッドも同時に生成、定義してくれる。

 ゴーストメソッドを使うと、WebサービスのAPIを静的にメソッドにマッピングせずに、メソッドとして受け取ったものを動的にAPIに整形するといったこともできる。Perrotta氏の書籍では例としてFlickrのAPIが挙げられている。Flickr側のAPIに新たに何かが追加されたとしても、ライブラリに変更を加えることなく、Rubyのメソッドとして利用できてしまう、というわけだ。こうした手法をPerrotta氏は「動的プロキシ」と名付けている。

 動的プロキシのような使い方では、継承している基本クラスなど、上位クラスのメソッド名と衝突する可能性がある。このためすべてのオブジェクトの基底となるクラスから必要最小限のメソッドを残して片っぱしからメソッド定義をクリアするイディオムもRails内部では常套句となっている。これをPerrotta氏は「ブランク・スレート」と呼ぶ。

 「クラス・マクロ」とPerrotta氏が名付けたものは、RubyでもRailsでも広く使われている。

classmacro.jpg クラス・マクロは宣言的な構文でクラスメソッドを呼び出す手法

 RubyやRailsでは、クラス定義の先頭のほうに宣言的に呪文が書かれていることがある。多くの場合、クラスが持つべき性質や、フックの定義が書かれている。例えばRuby標準の「attr_accessor :var」は、あるクラスのインスタンス変数「var」のゲッターとセッターを定義する宣言だ。これは宣言文や言語標準の予約語に見えるが、実はModuleクラスのプライベートメソッドに過ぎず、:varも引数としてシンボルが渡されているに過ぎない。表面下では、ゲッターとセッターを定義するコードが動的に走る。

 Rubyのattr_accessor(と、その同類)は、Cで実装されているが、Rubyプログラマは“クラス・マクロ”を使って同様の拡張を自分で行える。例えばRailsのバリデーションは、モデルクラスに「validates_uniqueness_of :username」などと宣言することで行うが、このvalidates_uniquness_ofは、実はActiveRecordが定義しているクラスメソッドだ。モデル間の対応関係を定義する「has_many」「belongs_to」なども、クラス・マクロによるDSLの実装例だ。

メタプログラミングは強力すぎるか?

 このほかにもスペルの例として、メソッド名を指定して、その振る舞いに手を入れるための定型句や、変数のスコープを動的に操ることでコンテクスト間で変数を共有するテクニックなどがある。

 Perrotta氏はこうしたテクニックの数々に触れて戸惑いを覚えながらも、「ほかの言語で学んだことはいったん忘れることです。なぜなら、これはRubyであり、Rubyというのは、こうやって使うものだからです」と話す。Perrotta氏は、メタプログラミングを習得しなければ、Rubyの完全な力を引き出せないと考えているという。

perrotta.jpg

 Lisp系言語のマクロ、C++のテンプレートなども同様だが、メタプログラミングは強力すぎて危険だという指摘も多い。多用するとコードが追いづらくなるほか、少しの変更が全体に大きな影響を与えて予期しない動作を引き起こす可能性があるからだ。こうした懸念に対してPerrotta氏は、「もし使うのが危険だというのなら、私はむしろ、そのすべてを知りたいと思います」と、積極的に学ぶ道を勧める。著書でも、テクニックの紹介に先立って、Rubyのオブジェクトモデルやメソッド探索の仕組みの解説に、かなりのページ数を割いている。

 このPerrotta氏の著書『メタプログラミングRuby』の序文に寄せて、Rubyの設計者である、まつもとゆきひろ氏は、こう書いている。

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

 コアクラスも書き換え可能である「オープンクラス」というのが典型だが、Rubyではプログラマ自身が自覚している限り、破れないルールは少ない。例えば、クラス定義の中にある「private」以降に定義があるメソッドはレシーバを省略した形でなければ呼び出せないため、事実上クラスメソッドのように振る舞う。しかし、動的ディスパッチと呼ばれる引数にメソッド名を指定する方法だと、当該クラスと関係のないコンテクストからですら呼び出せてしまう。これは一見危険なことだが、このことによってクラスを動的に拡張して、一群のメソッドを追加するような“魔法”が、Rubyでは1行で実現できてしまう。Perrotta氏は、「Rubyの世界では、private()は命令ではなく提案だと考えられている。これはRubyの哲学に不可欠なものだ」と書籍の中で説明している。

 結局、メタプログラミングとは強力で危険な、特殊なプログラミング技法なのか? どこで使い、どこで使うべきでないのか?

 同書のエピローグには、禅問答のような短いやりとりが掲載されている。山の頂で瞑想している師匠に向かって、「メタプログラミングの真髄とは何ですか」と、ひと通り学び終わった弟子が問うシーンがある。Perrotta氏は、師匠にこう言わせている。

 「メタプログラミングというものなど存在しない。すべてはただのプログラミングじゃ。去るがいい。静かに瞑想させておくれ」

もっとDSLを使おう! 万葉の大場氏が講演

 “メタ”とかスペル(魔術)と言うと、何か特殊なテクニックに感じられる。しかし、実際にはメタプログラミングの概念は古くからあり、Rubyでも広く使われてきたという。多くのRubyistやRails開発者たちが、どこからどこまでがメタなのかなどと意識せずに、通常のプログラミング技法としてスペルを使っているという。

 中でも、解こうとしている問題領域に特化した語彙を定義する言語内DSLを作ることは、Rubyでは普通になっていて、多くのライブラリはDSLを提供している。では、DSLの設計・実装は誰がやるのか?

ohba.jpg 万葉 代表取締役の大場寧子氏

 RubyKaigi初日に「Rubyで作るDSLの基礎」と題した講演を行った万葉 代表取締役の大場寧子氏は、大ホールに詰めかけた聴衆に向かってこう呼びかけた。

 「DSLは、“すごい人”が書いて使わせてもらうものでしょうか? そうではないと思います。Rubyのコーディング技術の1つと考えればいい。この講演が、日常的にDSLっぽく書こうと考える、そういうきっかけになればと思います」

 Rubyを使えば、自然と何らかのDSLの利用者となる。しかし、大場氏が反語表現で問いかけたように、すべてのRubyistが自分でDSLを設計、実装しているわけではない。大場氏の講演は、そうした利用層に対して、もっと積極的にDSLを使ってみてはどうか、と呼びかけるものだった。「DSLは特別な存在ではありません。自分で取り入れられる身近な存在です。まずは気になったコードを読むのが良いでしょう」(大場氏)。

migrations.jpg 大場氏が示した典型的なDSLの例。Railsのデータベース管理で使うマイグレーションの例で、処理内容が自然言語のように簡潔で読みやすい

 大場氏は、“ふつうのRuby”と言語内DSLによる表現を比較すると、Rubyの基本はオブジェクト指向らしく「レシーバが、これを寄越せ、くれという構造」(大場氏)であるのに対して、DSLでは、

  • 宣言的記述
  • ブロック活用
  • 特定概念を表すメソッド

が、違うという。例として大場氏は、Boyクラスの性質として、「self.money = 0」を設定する宣言的記述として、「私ならpoorと書きます」と、“poor”という語彙を定義するような大胆な例を挙げた。「表現力は重要です」(大場氏)。こうしておけば、クラスの定義部分で1行「poor」と書いておくだけで、そのクラスに宣言的に特定の性質や属性、振る舞いを与えることができるようになる。

 ほかにも、複雑なデータ構造を表現するために、処理にスコープをかけるブロックの活用や、英語(自然言語)っぽく見せるための“it”や“should”の活用例など、大場氏は「DSLらしさ」を追求するテクニックや考え方を披露。実用性と妥当性をバランスさせながら設計していくことが大事だと講演を締めくくった。

DSLの実例:RackSpace

 DSLを設計・活用した事例として興味深かったのは、RubyKaigi2010の2日目に「Rocking the enterprise with Ruby」と題した講演を行ったThoughtWorks社のMunjal Budhabhatti氏とSudhindra Rao氏だ。

rao.jpg ThoughtWorksのMunjal Budhabhatti氏

 ThoughtWorksはアジャイル開発に力をいれているITコンサルティング企業。講演では多くのクライアントのうち、米国の大手ホスティング事業者「RackSpace」の事例を紹介した。Budhabhatti氏とRao氏の2人は、過去4年に渡ってRackSpaceに常駐し、サーバやネットワーク管理業務のシステムを、Rubyを活用したDSLを設計することによって効率化したという。

 「Ruby、エンタープライズと言えば、形容矛盾みたいに思えるかもしれませんが、そうではありません」(Budhabhatti氏)

 ThoughtWorksがRackSpaceとともに取り組んだ課題というのは、ホスティング事業者が日常的に行っている管理業務で、例えばIPアドレスの割り振りや回収、DNSレコードのデータベース管理、サーバの物理的な配置の管理、監視などだ。

 例えば顧客から、2つの異なるデータセンターに、それぞれ指定台数のサーバをセットアップしてほしいという依頼が来た場合には、ネットワーク機器の設定など多くの作業が発生する。あるいは「スタートアップ企業というのは、成長するもので、半年おきに20台、25台、50台とサーバ台数が増える。そのたびにマイグレーションが必要」(Budhabhatti氏)というようなケースだ。

4.5日のネットワーク管理業務を18分に短縮

 ThoughtWorksがパートナーとしてシステムを刷新するまで、RackSpaceでは、こうしたネットワーク設定の作業が発生するたびに、「ネットワークエンジニアが、異なるアプリを実行し、それをメモして、また実行してメモしてということを繰り返していた」(Budhabhatti氏)という。ThoughtWorksは、この分断されたシステムをドメインに設計したRESTベースのエンジンと、それらをDSLで組み合わせて利用するコンシューマ・アプリとする構成に変更したという。REST実装にはRuby on RailsのActionControllerを使い、ワークフローエンジンにはDSLで定義が書ける「Ruote」を、メッセージング・システムには「RabbitMQ」を使ったという。

rack01.jpg RackSpaceのシステム概要
workflow.jpg ワークフローエンジンには「Ruote」を採用。RESTでアクセスできる各エンジンを連携させた
rack02.jpg REST対応の内部APIの実装にはRailsのActiveResourceを活用したという
rack03.jpg テストコードの例

 顧客に効果を示すため、「まず比較的小さなプロジェクトとしてIPオートメーションから始めましたが、既製品利用に比べて40%のコストダウンとなりました」(Budhabhatti氏)という。徐々にアーキテクチャを進化させていく上で、Rubyの使い勝手、リファクタリングのやりやすさは重要で、「より速いサイクルで失敗を繰り返すことができた」(Budhabhatti氏)という。2週間に1度はRackSpaceと成果物を共有するアジャイルな開発スタイルを採用した。

 結果は、それまで9システムを使って4.5日かかっていた作業が18分で終わるという劇的な改善となったという。

なぜRubyだったのか?

 Rubyで性能や安定性に不安はなかったのかという聴衆からの問いに対して、「むしろMySQLやOracleが先にボトルネックになることがありましたね」と、Rao氏はエンタープライズ用途でもRubyが活用できるとした。また、なぜRubyを選んだのかという問いに対しては、「色々な側面がある。巨大なコミュニティがあるのが大きい。Rubyなら、困ったことがあっても聞けば答えてくれる大きなコミュニティがある。Javaにはそれほど大きなものはない」と話した。「それにJavaで書いたとしたら、もっとプロジェクトは長引いただろうね」。

 DSLをRubyで設計したことについては、「“ip.assign”というように、読めば分かるように書けるのでとても自然に感じるものができた」としている。

 きちんとテストを書く文化とツールがRubyコミュニティにある点も重要だという。

 「Rubyのようなオブジェクト指向は振る舞いをテストしやすい。Rubyには、非常に成熟したテストの文化とツールもあります。テストを書かなかったら、君は本当にRuby開発者かと言われてしまいますよね。テストが書きやすいというのは重要で、PerlやPHPだと“うん、テストね。後で書くね”となりがちです」

 Scala、Clojureなど、JVM言語は候補にならなかったのか? 講演が終わった2人に聞いてみた。

 「ScalaもClojureも非常に有望な言語だと思います。ただ、Scalaは機能が豊富すぎて、たくさんのゴミが詰まっているというのが個人的な印象ですね。Clojureは美しくてシンプルです。でも誰もWebアプリを書くのにClojureなんて使いませんよね? Railsがあるんだから、Railsでやればいいじゃんって話です。計算や重たい処理が必要な部分でClojureを使うなど、今後は、1つの言語が特定の1つのことを上手にやるという方向で進化していくのだと思います」

Ruby魔術の広がり

 多くのコンピュータ関連技術のトレンドは、過去の歴史の中に埋れていたものの“再発見”だという説がある。昨今流行の兆しを見せる仮想化や関数型言語などは、そうした例だ。

 メタプログラミングや言語内DSLも、Ruby on Railsの華々しい登場によって、大きく注目を集めて再発見された感がある。

 ただ、それは主に英語圏での話。「Rubyは国産だから」という理由から、非技術的な文脈でRubyやRailsが注目を集めることはあっても、Rubyの持つパワーが日本国内で本当に評価されているのかどうか、そこが記者にはよく分からない。そもそもRuby on Railsは日本に逆輸入されたと言えるほど、まだ広まっていないのではないだろうか。

 海外からRubyKaigiに参加したRails開発者に聞けば、その印象は強まる。例えば、シリコンバレーやサンフランシスコ界隈では、Rails開発者は引っ張りだこで売り手市場が続いているというし、オーストラリアからの参加者によれば、Rubyのメーリングリストにはほぼ毎日求人情報が投稿されるほど、という。日本で、そうした話は聞いたことがない。

 日本ではメタプログラミングや言語内DSLが持つ潜在的なパワーも、まだ一部の技術者にしか十分に認識されていないのではないか。Rubyの“魔術”に魅せられてRubyKaigiに参加している人々や、それを使いこなしている海外の事例を見ていて記者が感じたのは、そうしたギャップなのだった。

※記事初出時にMunjal Budhabhatti氏の名前をSudhindra Rao氏していました。お詫びして訂正いたします。

関連リンク

(@IT 西村賢)

情報をお寄せください:

Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

キャリアアップ

- PR -

注目のテーマ

ソリューションFLASH

「ITmedia マーケティング」新着記事

「ECプラットフォーム」 売れ筋TOP10(2024年3月)
今週は、ECプラットフォーム製品(ECサイト構築ツール)の国内売れ筋TOP10を紹介します。

GoogleがZ世代のローカル検索でInstagramやTikTokに敗北 なぜこうなった? これからどうなる?
Googleは依然として人気の検索サイトだが、ことZ世代のローカル検索に関しては、Instagra...

DE&Iに関する実態調査 「公平」と「平等」の違いについて認知度は2割未満
NTTデータ経営研究所がNTTコム オンライン・マーケティング・ソリューションと共同で実施...