APIデザインケーススタディ —— Rubyライブラリを移植する前に読む本

APIデザインケーススタディ 』という本を頂戴したので読んでみた。

ライブラリ作者に向けて

この本はRuby標準ライブラリを題材にして、分かりやすく、多様な機能をサポートして、互換性を保つAPIの設計をするにはどのように考えるべきかを教えてくれる。

ここでAPIと言っているのは、一般的なRubyのクラスとオブジェクトとメソッドから成るライブラリをどうデザインするか、という話である。 別にChef RecipeやRSpec DSLのようなちょっと変わったDSLを設計するとかそういう話ではない。確かにその種の言語内DSLのデザインには固有のセンスが必要とされるし、 Ruby DSL Handbook なんて本が書かれているように実装にあたってもある種のテクニックが必要なのも確かだ。でも、それ以外の「ふつう」のライブラリのデザインは果たして簡単だろうか。 適切な粒度のクラスを定義する。必要な機能に合わせてメソッドを定義する。メソッドの引数は何であるべきか。引数の順序は? 引数が多すぎて使うのが大変になっていないか。そのメソッド名は他の似た機能から類推可能だろうか。機能を追加するとき、どうやって後方互換性を保てば良いのか。互換性を破ってでも改善すべきと判断するにあたっては何を考慮すれば良いのか。 本書が扱うのはそうした話題だ。

ちなみに、著者のakr(田中哲)さんはRubyライブラリ設計の第一人者と言っても良いだろう。 長くに渡ってRubyコアおよび標準添付ライブラリの開発を幅広く手がけており、今のRubyコミュニティにあるライブラリ設計の慣習を基礎づけた人々の1人だ。 中でも本書で題材に上がっている IO クラスや Time クラス、および socket ライブラリの設計において、いくつもの困難な課題を解決してきた。

本書を読むことで題材になっているRubyの標準ライブラリへの理解は深まるだろうし、自分でRubyライブラリを書くときの考え方の参考にもなるだろう。 もっと一般に任意の言語でライブラリのAPIをデザインするときの助けにもなるだろう。

ライブラリ移植者に向けて

本書はまた、ライブラリを移植する物語として読むこともできる。

Rubyの標準ライブラリ、ことに IOsocket はC標準ライブラリやUNIXシステムコールを意識してデザインされている。 よってこれらのライブラリをCからRubyへの移植と捉えることもできるだろう。

このようなライブラリの移植に当たっては、幾つものデザインの選択肢がある。一番単純なのは、元のライブラリのAPIを引き写すことだろう。 利点としては元のライブラリを知っていれば移植先の学習が容易なことが挙げられる。それにデザインにあまり頭を使わなくて済む。 一方で、これは一般にあまり使いやすいAPIにはならない。 言語ごとに望ましいエラーの返し方も関数の命名慣習も異なるし、そもそも慣習を支える言語の特性自体が静的型付けの有り無しから真偽値の定義から何もかも違うのだ。

その対極に当たるアプローチは、ほぼ同じ機能を提供するライブラリをゼロからデザインすることだ—— もはや「移植」とは言いがたいかもしれないが。 利点としては、もし適切にデザインできれば移植先の言語のプログラマにとって理解しやすく使いやすいAPIが得られる。 一方で、移植元のライブラリデザインからの類推が働かないので学習コストがかえって高く付く可能性もある。

ではRubyは、akrさんは、この両極の間でどのような選択をしてきただろうか。本書はその歴史を語っている。

よりよい移植をめぐって

話は変わるが、私はこの数年というもの、業務ではRuby以外の言語で開発をすることが多い。 もっぱらC++PythonやGo, 時にはJavaを触り、やむを得ずPHPを触ることもある。

これらの言語で開発する過程でしばしばRubyのライブラリやフレームワーク、ツールから影響を受けたと思しきものを目にすることもあった。 ことに各所へのRailsActiveRecordの影響は著しいものがある。昨今ではRailsが常にWebアプリケーション開発の第一選択肢とは限らなくなってきているのも、ひとえにRailsの良さが他のフレームワークに広く受け入れられてもはや現代webアプリケーションフレームワークでは当たり前の性質となっているからである。

このようにRubyコミュニティの成果が他から参考にされるのは誇らしくもあるが、一方でうんざりさせられることもある。 うんざりするのは、Rubyのライブラリやフレームワーク劣化コピーを目にするときだ。

たとえばRails劣化コピーである。 いまどきRailsに影響を受けたフレームワークは珍しくないが、さて、果たしてそのRailsの機能は本当にその言語に向いているだろうか。 そもそもRailsフルスタックかつ密結合な極端なフレームワークとして登場した。Railsには(当時のwebアプリケーションの)すべてが揃っていて、その構成要素が密に結合している代わりに「Railに乗っている」限りにおいては極小のコード量であらゆることが簡単にできた。 普通に考えれば構成要素が密結合なのは悪だが、Railsは密結合由来の難点をうまく補うような機能を提供していて、またRuby自体の柔軟性をもって(いざとなればモンキーパッチしてでも)問題を解決することができる。 あんなにも密結合なフレームワークがまともに使い物になること自体が信じがたいけれども、うまいバランスとRubyの力で不思議と使いやすいのがRailsである。

だとすれば、Rubyよりも柔軟性に劣る大抵の言語にとってRailsの採った選択は適切でない。また、「Railsのサブセットを移植した使いやすいフレームワーク」というのも難しい。全体が揃ってうまくバランスが取れているのがRailsだからだ。 Rubyよりも実行時の柔軟性に欠けるその言語にはRubyよりもよい型安全性があるかもしれない。その言語では標準的なORマッパーがActiveRecordと異なるデザインかもしれない。それならば、おそらくRailsとは違うデザインが向いているはずだ。 それにも関わらず 「Railsをgoで書きました」「RailsPHPで書きました」という類のデザインにはうんざりさせられる。

Rubyのactive_recordライブラリもまた多くのフォロワーを持つ。 しかし思い出して欲しいのは、そもそもの PofEAA ではActive Recordの適用領域は限定的だとされているし、active_recordライブラリはActive Recordパターンより遥かに多くのものを提供しているということだ。 active_recordライブラリやそれに依存するRailsが複雑なアプリケーションを書くのにも使えるのは、そうした拡張機能や様々な細かい配慮、定義済みのRakeタスクのお陰である。 これらなくして、ただactive_recordに影響を受けてActiveRecordパターンを複雑なアプリケーションに採用するのは無謀きわまる。

さて、ではどうすれば良かったのだろうか。そのヒントを本書に見ることができるだろう。 本書はRubyのライブラリは何を考えてデザインされているのかを解説している。またライブラリ移植の事例を豊富に提供している。 よって、何がRuby固有で、何かを移植するときは何を考慮しなければならないのかを学ぶのにうってつけである。

ああ、Goのあのライブラリの、PHPのあのフレームワークの、Pythonのあのツールの作者が安易にRubyのやり方を流用する前に本書を読んでいてくれたら、あれらもまともに使える代物であったろうにと思わずにはいられないのだ。