「Cookpad Ruby Hack Challenge」とは
「Cookpad Ruby Hack Challenge」は、Rubyというプログラミング言語がコンピューターの上でどう動いているのかを知り、ハックしてみるイベントです。8月30日から31日にかけて2日間、クックパッドのオフィスにて開催されました。
このイベントは「Rubyのインタプリタをハックする」という超ディープな内容なのですが、なんと10人の募集枠に約100人もの応募があったのだそう。すごい。今回の参加者は、すこし応募枠を拡大して15人ほどでした。
そんなディープな超人気イベントを、Rubyは好きだけど「インタプリタ」の「イ」の字も知らない私池澤がレポートします!
Rubyに日常的に触れている方は、一度は「Rubyってどうやって動いているんだろう」と思ったことがあるのではないでしょうか。
Rubyで書かれたプログラムは、Rubyのインタプリタ「MRI(Matz Ruby Interpreter)」で解釈され、実行されます。
このインタプリタに機能を追加したり、改良したり、性能を向上させるにはどうすればいいのかを学んでいきます。
今回講師をしてくださるのは、Rubyのコアコミッタである笹田耕一さん。笹田さんは、Rubyがより早く動くためのエンジン「YARV」の開発者で、現在はクックパッド社でフルタイムでRubyを開発しています。
インタプリタを知れば、プログラミング言語の仕組みがわかる!
インタプリタとは一体何をしているのでしょうか。インタプリタというと何やら難しいことをやってそうに聞こえますが、つまり「プログラムを読んで実行する」ということをしています。
具体的には、まずプログラムをパースしてツリー構造(抽象構文木、AST)に、そして更にそれをいわゆる「コンピューター語」であるバイトコードに変換します。これが「読んで解釈する」部分。
そしてそのバイトコードを、評価器(evaluator)が読み、実行していきます。これが「プログラムを実行する」部分となっています。
これはRubyに限定したことではなく、JavaやPythonなどの言語も、似たような仕組みで動いています。
▲笹田さんのスライド「Cookpad 17 day Tech internship 2017 言語処理系入門 Rubyをコンパイルしよう」より。ちなみに笹田さんは「YARV」を開発し、バイトコードの解釈をいかに高速化するかという課題に取り組まれています。Rubyが高速化したのも笹田さんのおかげ!
ちなみに、Rubyが誕生してから今年で24年になります。「24年間も開発されていれば、あまり変更されるところもないのでは?」と思う方もいらっしゃるかもしれませんが、プログラミング言語をとりまく環境(例えば、Rubyが動いているOSであったり、コンピューターの性能であったり)というのは目まぐるしく変わっていきます。
そして、ユーザーが求めることも変わっていきます。それに合わせて、現在もなお開発が続けられているというわけです。
Matzが語る、100年先を見据えた言語設計とは
イベントでは、Rubyの生みの親であるまつもとゆきひろさんの特別講義も行われました。
笹田さんからの「いい話をしてください」というオーダーに対し、「Ruby開発と互換性」についてお話されていました。
まつもとさんによると、ブログラミング言語は、常に進化していないといけないけれど、進化しすぎてもいけないというパラドックスを抱えているそうです。
なぜなら、ユーザーは刺激を求めて新しいものを好む傾向にあるけれど、使う人が増えれば増えるほどコミュニティは言語の変化を好まない傾向にあるからです。
例えば、PHP5からPHP6へのアップデートでは、かなり革新的な変更を行ったらしいのですが、革新的であるがゆえにコミュニティに受け入れられず、最終的にはPHP6自体が廃止になったという過去があります。
Rubyは、目先の流行に流されず(今でいうと静的型の導入など)、100年先も通用するような言語を目指して、倦まず弛まず開発が続けられています。
ちょうどRubyコミッターが集まる会議もクックパッドで開催されており、会場にはたくさんのコミッターが集まりました。
イベント参加者がコミッターの方に質問をしまくる「コミッターQ&Aセッション」も行われました。
Rubyの中身をいじってみよう!
それでは、笹田さんが行ってくださった講義の内容を少しご紹介します。
まずは、Rubyのなかを覗いてみましょう!
SubversionやGithubからRubyをダウンロードしてくると、Rubyのなかを見ることができます。
「RubyはCで動いている」と聞いたことがあるかもしれませんが、ディレクトリの中を覗いてみると、確かにそこにはCのプログラムが。
今ダウンロードしてきたRubyを使えるようにするには、このCのプログラムをビルドしてコンパイルしてから、インストールする必要があります。
▲今回のディレクトリ構造。rubyにはRubyのソースコード、buildには、コンパイルしたコード、installには、Rubyをコマンドラインから使えるようにするためのプログラムが入ります。
$ cd rubyhackchallenge
$ git clone https://github.com/ruby/ruby.git # GitHubからRubyのソースコードを取得します
$ cd ruby
$ autoconf
$ cd ..
$ cd build # ビルドディレクトリに移動
$ ../ruby/configure --prefix=$PWD/../install --enable-shared
▲LinuxやMacでのコマンド例です。 私はrbenvでバージョン管理をしているので、インストールディレクトリは.rbenv/ruby/rubyhackchallengeとしました。
さて、まずは簡単なカスタマイズをしてみます。
ruby -v
というコマンドを打つと、現在使っているRubyのバージョンが表示されます。この部分のカスタマイズに挑戦してみましょう。
Rubyのディレクトリを見てみると、version.h
とversion.c
というファイルがあることが分かります。いかにもバージョン定義に使ってそうなファイル名ですね!
さらにversion.c
を見てみると、ruby_show_version
というメソッドが定義されています。こちらもいかにもバージョン情報を出力していそうなメソッド名です。
実際に、ここにprintf()
を書き加えてみましょう。
/*! Prints the version information of the CRuby interpreter to stdout. */
void
ruby_show_version(void)
{
PRINT(description);
#ifdef RUBY_LAST_COMMIT_TITLE
fputs("last_commit=" RUBY_LAST_COMMIT_TITLE, stdout);
#endif
#ifdef HAVE_MALLOC_CONF
if (malloc_conf) printf("malloc_conf=%s\n", malloc_conf);
#endif
printf("nyaan\n"); // 追加
fflush(stdout);
}
書き終えたら、コマンドラインからビルドしてコンパイルしてRubyを実行します。
$ make install
▲ 無事表示されました!
次に、ruby 2.5.0dev
のrubyと表示されている部分を変更してみましょう。これはどうやらruby_show_version
メソッドのなかのPRINT(description);
という部分で出力されていそうです。
description
を辿ってみると、最終的にはversion.h
で定義されているようです。
# define RUBY_DESCRIPTION \
"ruby "RUBY_VERSION \
RUBY_PATCHLEVEL_STR \
" ("RUBY_RELEASE_DATE \
RUBY_REVISION_STR") " \
"["RUBY_PLATFORM"]"
この「ruby」と書かれている部分を、例えば「python」と書き換えて、先ほどと同じようにビルド&コンパイルして実行してみます。
▲無事表示されました!
このイベントに参加する前までは、Rubyのハックはかなりハードルが高いと思っていたのですが、意外と簡単にハックデビューできるんですね!
ここで豆知識です。プログラムを書き換えるたびに、ビルド&コンパイルしてから実行するので、それなりに時間がかかってしまいます。
そんなときに、Rubyが作られる過程でつくられる、拡張ライブラリを読みこまない機能制限版のRuby「miniruby」を利用することも可能です。こちらを使えば、変更した結果を素早く確認することができます。
$ make miniruby # minirubyとしてビルド&コンパイル
$ ./miniruby hoge.rb # minirubyを使ってhogeプログラムを実行する
また、rubyディレクトリにtest.rb
を用意していた場合、以下のコマンドでminirubyでtest.rb
の実行まで行うことができます。
$ make run
オリジナルメソッドを追加してみる
続いては、オリジナルのメソッドを追加してみましょう。Rubyでは、Array#first
というメソッドがあります。array.first
というプログラムを書くと、配列「array」の一番最初の値を返してくれます。
ここにarray.second
というメソッドを加えてみましょう!
配列に関する定義は、array.h
とarray.c
で行われています。
array.c
にsecond
メソッドを追加します。
static VALUE
ary_second(VALUE self)
{
return rb_ary_entry(self, 1);
}
▲array.c。Init_Arrayの上くらいにこのメソッドを書き加えます。
rb_define_method(rb_cArray, "second", ary_second, 0);
▲array.c。Init_Arrayメソッドの中にこれを書き加えます。
テストも書いておきましょう。test/ruby/test_array.rb
がテスト用のファイルになります。
test_first
メソッドの下あたりに以下のコードを加えます。
def test_array_second
assert_equal(4, @cls[3, 4, 5].second)
end
▲test/ruby/test_array.rb
test.rb
にsecondを使ったプログラムを書き、実際に動かします。
[1, 2, 3].second
▲test.rb
make run
してみると、無事secondが使えるようになっていることが分かります。
私自身、「プログラム言語の中身って、なんかよく分からないけど難しそう…」というイメージが強かったのですが、実はこういった部分は単純なメソッドの集合体なんですね。
既存のRubyを少しハックするくらいであれば、意外に簡単にできちゃいそうです。
当日使用した講義資料には、より詳しい解説や追加課題が載っています。
ぜひこちらも読んでみてください。
Rubyを魔改造してみる
イベントでは、こうしたちょっとしたRubyのハックを学んだのち、個人個人で課題を見つけて取り組みました。
Rubyのバグを報告するフォーラムに載っているバグを修正する方、デバックに役立つ拡張プラグインをつくる方、など、有意義な課題に取り組む方が多いなか、私は今実行するメソッド名を読み上げるようにRubyを魔改造しました。
Macの標準機能であるSayコマンドを利用したハックだったので、このハック自体はあまり高度なものではありませんでしたが、入力されたコマンドを理解する部分であったり、笹田さんが開発されている「YARV」のコードを一部読んだりすることで、Rubyのしくみの真髄を覗けたような気がして、かなり刺激的でした!面白かったです!
▲まつもとゆきひろさんとコアコミッタの中田伸悦さんにデバックのアドバイスをもらうという……一時なんとも贅沢な展開に……。
求む、新しいコミッタ!
今まで、コミッタになることは死ぬほど高いハードルだと感じていました。
しかし、コミッタになるということは、意外とこうしたハックの延長線上にあるものなのかもしれません。
Rubyのハックに興味を持った方は、今回のイベントの資料はすごく分かりやすくまとまっているので、ぜひ一読してみてください。
今回の記事では紹介しきれなかった拡張機能の作り方や、デバックの仕方などにも触れられています。
また、Rubyのインタプリタについて書かれたサイトや書籍もいくつかあります。
-
書籍『Rubyのしくみ』(Rubyのしくみの解説本)
-
書籍『Rubyソースコード完全解説』
(Rubyのしくみを完全網羅した解説本。既に絶版していますが、Webにすべて公開されています。ただし、解説されているRubyのバージョンは、1.8と少し古いものになっています。ちなみに、笹田さんがコミッタになったきっかけは、書籍『Rubyソースコード完全解説』を読んで「これなら貢献できるかも」と感じたことなのだそう)
* サイト『Rubyの拡張ライブラリの作り方』
(Rubyの拡張機能のつくりかたがまとまっています)
そして、笹田さん自身も「こうしたイベントは定期的に開催したい」とおっしゃっていたので、クックパッドの開発者ブログやconnpassは今後も要チェックです!
以上、池澤でした。
【PR】あなたの本当の評価がわかる!年収提示サービス「moffers」
登録した詳細な経験・スキル情報に基づき、企業が「年収」を明示した形でスカウトを行う、エンジニアのための転職サービス「moffers」がオープンしました!
あなたの本当の評価がわかる「moffers」に登録してみませんか?
【moffers参加企業】
日産自動車、富士通、本田技研工業、富士通、三菱電機、サイバーエージェント、ソニーネットワークコミュニケーションズ、リクルートテクノロジーズ、ほか
WebからIoT、自動運転、AIまで幅広い業種・職種がラインナップしています。この機会にぜひご登録ください!