tDiary 5.1.0にアップデートした(前田修吾さんが)。あまり久しぶりなので、設定変更と確認しようとしたら、なにもかも忘れていて情けない。
Herokuに入社してもうすぐ8年になるのだが、ようやくアカウントを取得した。各方面で呆れられていた状態が解消した。
しかし、まだ使い方がよくわからないのであった。
いやあ、Ruby開発者の日記だというのにまだRuby 1.8で動作していて、恥ずかしい状態だったこの「Matzにっき」だが、前田修吾さんのご尽力で Heroku に移行しました。バージョンも 2.6.2 へ。
これで恥ずかしくない。 最近は、主に Twitter か、Quora で書いてたけど、そろそろここも更新を再開するべきか。
久しぶりの更新
GitHubの <URL:http://github.com/matz/streem> を公開したら驚くべき反響の大きさなので、本人もびっくりしている。
ので、ここでちょっとまとめておく。
自分でもなにを言っているのかと思うけど、雑誌という出版物と比較して、超スピードとかでは説明のつかないネットとOSSコミュニティのバズり方を見た思いがする。
一方、雑誌連載のサンプルであるStreemだが、言語仕様についてはパッとした思いつき、 というわけでもなくて、数カ月間、このような言語はあまりないし、実用的価値もありそうと思って作っている。
特徴
雑誌の連載のネタだとはいえ、今後も開発をやめるつもりもないので、ほそぼそと進歩し続ける予定。
っていうか、現在 C でタスクスケジューリングを実装することの困難さに絶望しつつあるので、 マジで streeem を参考に Go で実装するかも。
Twitterで質問を受けたので、 mrubyのmrb_gc_arena_save()/mrb_gc_arena_restore()の使い方 という解説を行った。が、1つ140文字のTwitterでの解説にはどうしても無理があるので、 こっちでまとめることにする。
まずは、Twitterの発言*1はこんな感じ。
arenaの目的。利用中のオブジェクトはGCに回収されないよう保護する必要がありますが、Cのスタックはポータブルに参照できません。そこでC関数実行中に生成したオブジェクトは全部「生きている」とみなす事で保守的に保護します。 yukihiro_matz 2013-07-31 08:16:45
arenaの目的(2)。この保護のためにオブジェクトを記録しておく領域がarenaです。mrubyではデフォルトで100個のオブジェクトを登録できます。 yukihiro_matz 2013-07-31 08:19:02
save/restoreの仕事。現状arenaのサイズは固定なのでC関数実行中にあまり沢山オブジェクトを生成するとarenaが溢れます。そこで沢山オブジェクトを生成する前後にsave/restoreを置くことでarenaのサイズを復元し、溢れを回避します yukihiro_matz 2013-07-31 08:27:02
save/restoreの使い方。オブジェクトを生成する領域の前後をsave/restoreで囲みます。ただし、囲まれた範囲内のオブジェクトが保護されなくなりますから、どうしても必要なものはrestore後mrb_gc_protect()で改めて保護してください。 yukihiro_matz 2013-07-31 08:33:61
これを再度まとめてみよう。
mrubyをCで拡張していると「arena overflow error」という「謎のエラー」に悩まされることがある。 これはmrubyで「保守的GC」を実現している「GC arena」という領域があふれたというエラーだ。
GC(ガーベージコレクター)は、オブジェクトがまだ「生きている」、 つまり、プログラムのどこかから参照されているかどうかを判定する必要がある。 これはルートと呼ばれる参照から直接・間接に参照可能かどうかで判別する。 ルートには、ローカル変数・グローバル変数・定数などが含まれる。
プログラムの実行がmruby VMの中でおさまっている時にはこれは問題ない。 VMの持つルートはすべてGCからアクセス可能だからだ。
問題はCで記述された関数を実行中の時。 Cの変数から参照されたオブジェクトも「生きている」わけだが、 mrubyのGCはCの変数の内容を感知できないので、 C変数からしか参照されていないオブジェクトは死んでいると誤解してしまう。
まだ生きているオブジェクトを回収してしまうのは、GCとしてもっともやってはいけないバグだ。
CRubyでは、Cのスタック領域を無理やりスキャンすることで、 Cの変数をルートとしてチェックしている。 もちろん、Cのスタックを単なるメモリ領域としてアクセスするわけだから、 それが整数を意味する値なのか、ポインタ値を意味する値なのか判別することはできない。 しかし、まあ、そこは「ポインタのように見える値は安全側に倒してポインタだと思って処理する」という 方針で処理している。この「安全側に倒す」というポリシーのことを「保守的」と呼ぶ。
され、このようなCRubyの「保守的GC」にはいくつか問題がある。
その最大のものは「移植性のある方法でスタック領域にアクセスする方法がない」ということだ。 つまり、移植性の高さを実現しようとするmrubyのような処理系では使えないってこと。
そこで、mrubyは別の方法で「保守的GC」を実現した。
問題なのは、C関数実行中に生成されたオブジェクトで、 Rubyの世界から参照されてないオブジェクトのうち、 C変数からは参照されているのでまだゴミ扱いしてはいけないものが存在する、 ということだ。
既に述べたようにCRubyは、Cスタックをスキャンしてゴミのように見えるがゴミでないものを保護している。
しかし、その方法が使えないmrubyは、より保守的なポリシーを採用した。 つまり、C関数実行中に生成されたオブジェクトは、極端に安全側に倒して、いっそ全部生きているとみなせば、 少なくともゴミでないものを回収してしまう問題は回避できるんじゃないかと。
この結果、本当はゴミであるものを回収できないので、若干効率が下がることになるが、 移植性が高いまま、保守的なGCを実現できることになる。 CRubyで時々発生する「最適化で参照が削除されてゴミでないのにGCされた」問題とも無縁になる。
このポリシーで、「C関数実行中に生成されたオブジェクト」を登録しておくテーブルが 「GC arena」である。arenaはスタック状になっていて、 C関数の実行が終わるとその間に登録されたオブジェクトはポップされる。
原則としてはこれだけで、普通の場合は、これでめでたしめでたしなのだが、 GC arenaの存在は別の問題を引き起こすことがある。 これが前述した「arena overflow error」だ。
メモリが少ない環境でも動作することを考慮したmrubyはarenaのサイズを固定長にしており、 しかも、そのサイズはデフォルトで100とかなり小さめに設定されている。
実は当初はサイズ1000とかちょっと大きめにしていたのだが、 このテーブルサイズが厳しい環境があったのと、 後述するようなテクニックを使い、適切にarenaを管理すれば100でも普通に動くので、 現状は100にしている。
その結果、C関数の実行中に数多くのオブジェクトを生成すると、 arenaがあふれることになる。
その対策に用いるのが、表題のmrb_gc_arena_save()とmrb_gc_arena_restore()というふたつの関数である。
int mrb_gc_arena_save(mrb)はGC arenaの現在のスタック位置を返し、 void mrb_gc_arena_restore(mrb, idx)はarenaのスタック位置を保存された位置に戻す。
int arena_idx = mrb_gc_arena_save(mrb); ...なんかオブジェクトを作る処理... mrb_gc_arena_restore(mrb, arena_idx);
というような使い方をする。
もともとのC関数の実行は、このようにsave/restoreに囲まれているのだが、 一時的にオブジェクトを作り、その後は不要になる領域を 明示的にsave/restoreを囲むことにより、arena overflowを避けるわけだ。
とはいうものの、具体例を見ないとわからないケースもあるだろう。 ここでは、Array#inspectのソースを見てみよう。
static mrb_value inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list) { mrb_int i; mrb_value s, arystr; char head[] = { '[' }; char sep[] = { ',', ' ' }; char tail[] = { ']' }; /* check recursive */ for(i=0; i<RARRAY_LEN(list); i++) { if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) { return mrb_str_new(mrb, "[...]", 5); } } mrb_ary_push(mrb, list, ary); arystr = mrb_str_buf_new(mrb, 64); mrb_str_buf_cat(mrb, arystr, head, sizeof(head)); for(i=0; i<RARRAY_LEN(ary); i++) { int ai = mrb_gc_arena_save(mrb); if (i > 0) { mrb_str_buf_cat(mrb, arystr, sep, sizeof(sep)); } if (mrb_array_p(RARRAY_PTR(ary)[i])) { s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list); } else { s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]); } mrb_str_buf_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s)); mrb_gc_arena_restore(mrb, ai); } mrb_str_buf_cat(mrb, arystr, tail, sizeof(tail)); mrb_ary_pop(mrb, list); return arystr; }
実際のコードをそのまま引用してきたので、若干複雑になっているが、 Array#inspectの処理の本質は、 配列の各要素をinspectメソッドを用いて文字列化した上で、 それらを結合して配列全体のinspect表現を作ることにある。
全体のinspect表現の文字列を作ってしまえば、 処理途中に生成した各要素の文字列はもはや不要になる。 ということは、GC arenaにこれらのオブジェクトを登録しておく必要もない、ということだ。
そこで、ary_inspect()関数では、
という手順により、arena領域の消費を抑えている。
ここで注意すべき点は、最終結果となる配列inspect表現となる文字列は、 mrb_gc_arena_save()の呼び出しよりも前に生成していることである。 こうしないと、必要なオブジェクトがGCで回収されてしまうことになる。
処理のパターンとしては、さまざまな一時オブジェクトを生成した上で、 そのうちの一部だけを参照し続けるというケースも考えられる。 このようなケースでは、ary_inspect()のように既存のオブジェクトに結合するような手は 使えないので、mrb_gc_arena_restore()の呼び出しの後に mrb_gc_protect(mrb, obj)を呼び出して、そのオブジェクトをarenaに再登録する必要がある。 ただし、mrb_gc_protect()は注意して用いないと、これ自身がarena overflow errorの原因になることがあるので 注意するように。
などということを、Twitterだけで説明するのは現実的じゃないよなあ。
あ、そうそう。
このmrubyのAPIには、改善が必要だなと思ってる点があって、 その最大のものは、トップレベルでmrb_funcall()を呼ぶと GC arenaに戻り値が登録されるのでそのうちarena overflow errorになることだ。
戻り値を使わないmrb_funcall()のようなものを用意すればいいんだと思うんだけど。 いい名前が思いつかないんだよなあ。
*1 tDiaryにTwitterの発言を引用するプラグインが欲しいなあ
先日、Webronza というところに寄稿したのだが、有料登録しないと後半が読めなくなっていた。で、交渉して公開許可を頂いたので、ここで全文掲載。
「ちょっと待った!小中学校でのプログラミング教育」
現代社会はもはやコンピュータがなければ成り立ちません。そして、コンピュータは誰かが作ってソフトウェアがなければ、まったく役に立ちません。コンピュータは自発的に仕事をしてくれないどころか、誰か人間がソフトウェアという形でどのように仕事をすれば良いのか教えてやらなければ、なんの働きもできないのです。コンピュータが社会に役に立っているのは、ソフトウェアがあるからです。
どんなに賢いように感じられるコンピュータでも、自らソフトウェアを開発することはできません。コンピュータは単純な計算をものすごく速く行うことができますし、それを積み重ねることで人間を越える能力を備えていますが、その一方で、なにか新しいことを創りだすなどの創造的な活動は苦手です。はっきりいうとまったくダメだと言ってもいいでしょう。当面の間は人間がソフトウェアを作って、コンピュータに仕事を教えてやるしかないのです。
社会におけるコンピュータの重要性は明らかで、そのコンピュータがソフトウェアがなければ役に立たず、そのソフトウェアは人間にしか作れないとなれば、ソフトウェアを開発する人間こそが真に重要だということになります。しかし、現状、誰でもがソフトウェアを開発することができない以上、ソフトウェアを開発する人、プログラマを養成することは急務です。とは言うものの、どのような教育を行えば、優秀なプログラマを育成できるのかについて誰でもが合意できる方法はまだ見つかっていないようです。
最近、注目されているのが若い人たち、特に小学生や中学生へのプログラミング教育です。確かに優秀なプログラマは若い頃からその才能を発揮している人が多いようです。また、プログラミング能力はあまり年齢に関係ないようで、天才と呼ばれる小学生プログラマがいると思えば、70歳をはるかに超えて現役で活躍する方もいらっしゃいます。
そこで、若いプログラマを育てるために、小学校や中学校での情報処理の教育やプログラミング教育に力を入れようという動きもあるようです。しかし、自分自身のプログラマとしての経験から考えると、これにはなかなか困難がつきまとうように思えます。第一の課題は「誰が教えるか」という点です。学校をプログラミング教育の現場にするためには、当然のことながらプログラミングを教える教師が必要です。しかし、現在の小学校・中学校の教員でプログラミングの能力を持つ人はごく少数でしょう。もちろん、教科書通りに教えることができる人は短期間で用意できるかもしれませんが、それでは子供たちにプログラミングに前向きな気持ちを伝えることは困難でしょう。中学生時代にプログラミングをはじめた私自身も含めて、若い頃からプログラミングに「はまった」人たちは、結局、コンピュータを使いこなすのが楽しいからこの道に進んだようなもので、教科書に書いてあるから、あるいは学校の授業だからという理由でプログラミングをはじめた人など見たことがありません。プログラミングを教えるというのであれば、少なくとも教える人はプログラミングの楽しさを自覚している人でなければ成果をあげられないと思いますし、そのような人をそれぞれの学校に配備するのは大変な困難ではないかと思います。
第二の課題は「どのように評価するか」ということです。学校の授業であるということは、なんらかの評価をする必要があるわけですが、これがまた困難です。まず、プログラミングの能力は、創造性をともなうという点である種芸術に似ています。そういう点では美術の授業に似ているのですが、問題はプログラミングの場合、人によっては非常に短期間で上達するため、小学生であってもプロを越える作品を完成させることがたびたびあることです。人によって出来栄えが10倍、100倍あるいは1000倍も違うようなものを学校の成績としてどのように評価したらよいのか途方にくれます。
第三の課題は「なにを教えるか」ですが、この点は最初のふたつの課題に比べればなきに等しいものです。
では、学校教育はプログラマ養成にふさわしくないのであれば、いったいどうすれば未来を担うプログラマを養成することができるでしょうか。
正しい答えは私自身も持っていないのですが、私の知っている優秀なプログラマのほとんどが自学自習でプログラミング能力を身につけている現実を考えると、あるいはプログラマとは、養成されるものではなく、発見されるものなのかもしれません。
ある調査によれば、大学におけるプログラミング教育の受講者の内6割程度はプログラミングの基本的な部分の理解に困難を感じ、それは他の学科の成績に必ずしも相関しなかったのだそうです。だとすると、世の中にはプログラミングに向いた性格の人がいて、成績その他では区別することができないことを意味します。であるならば、少しでもたくさんに人にプログラミングに触れる機会を与え、そしてそれに興味を持てた人、才能の片鱗を見せた人にはより豊かな機会を与えるようなプロジェクトを総合的に設計することで、未来のプログラマを「発掘」できるのかもしれません。そして、そのような才能あふれる人たちに適切な待遇を与えることが、このIT社会の競争力を強める最大の方策なのかもしれません。
一年ぶりの更新か。「年刊Matzにっき」だな。
今年もニューオーリンズで開催されたRubyConf。同じ都市で二度開催は初めて。
で、しょっぱなが私のキーノート。
まあ、あんまり語ることはないので。スライドを見てもらおうか。
こんな感じ。
角谷さんオススメのspeakerdeck.comも使ってみた。
で、最後に「Diversity(多様性)」である。
初日のDave Thomasのキーノートでも3つの重要なこととして、 Diversifyをあげていた。多様性は重要なのである。
とはいえ、多様性はいいことばかりでもない。 Rubyにおける多様性といえば、昨今数々登場している別実装である。
などなど、多くのRuby実装がある。これら以外にも「Rubyっぽい言語」まで含めると 本当にいくつあるのか見当もつかない。
昔はPythonの人たちに「Ruby(とPerl)は複雑すぎて、別実装は登場しそうにない。 Pythonを見てみろ、CPythonとJytonとIronPythonがある」などと言われたものだが、 今や別実装の数ではRubyの方がしのいでいる。Pythonの方もPyPyとか新しいものも登場してるが。
が、一方、このような多様性にはコストがかかる。 まあ、プラットフォームの違うJRubyはおいておくとしても、 CRubyとRubiniusとMacRubyで分散しているリソースを集約すれば、 もっと早く言語(実装)が進歩するような気もしないでもない。
しかし、オープンソースプロジェクトでは結局はそれぞれの参加者がやりたいように関わって モチベーションを維持することの方がはるかに重要だ。
多様性は善で、コストは必要経費であるというのが私の認識である。
さて、多様性は善であるので、さらにそれを豊かにするために 私自ら新たな処理系を送り込もうと思う。
それは RiteVM であり、組み込みなど小規模なデバイス向けをターゲットとした処理系である。
現在のRubyは元々Unixをベースにして開発されたものであり、 UnixやPOSIX APIを提供しないような小さなデバイスや、 アプリケーションへの組み込みなどはあまり重視されてこなかった。
しかし、一方、組み込み分野などではデバイス性能の向上で ソフトウェアの比重が高まりつつあり、 実行速度やリアルタイム性がそれほど要求されない分野で Rubyのような「高級」な言語を使いたいという要求はそれなりにあるようだ。
そこで、以下のような処理系を新規に開発する
このVM開発のプロジェクトコードネームは Rite と称することにする。 RiteVMの開発は、経済産業省 平成22年度「地域イノベーション創出研究開発事業」の一環として行われる。
これにより、たとえば
などにRuby(のサブセット)が使えるようにする。
もちろん、今すぐにそうなるとは思っていないが、 今から準備すれば数年後には現実にできると考えている(ビジネス上の成功は別だけど)。
と、ぶちあげてしまったので、もう後に引けなくなったな。
前回に続いて 未来(≒Ruby 2.0)の話を。
今回、紹介した「未来」の機能は以下の通り。
今まで話してきたことじゃん、と思うでしょうが、その通り。 違いは
点です。特に後者は大きい。
Traitsの定義は
a trait is a collection of methods, used as a "simple conceptual model for structuring object oriented programs".
from Wikipedia (en)
ということで、モジュールとほぼ同じようなものです。 実際、今回導入するTraitsは言語要素の実体としてはモジュールを利用します。
ただ、モジュールの機能を取り込むのにincludeではない 別のやり方(mix)を導入することによって、includeが持ついくつかの問題を解消しよう というものです。includeの方が便利なこともあるので、includeもなくなりません。
includeの問題は
ことです。
擬似的な多重継承であるincludeは、 includeされたモジュールが継承ライン(ancestors)に含まれるようになります。 この時、状況によっては予測困難なことが発生します。
ひとつはincludeされた複数のモジュールで同名のメソッドが定義されていた場合、 その重複が意図されたもの(override)か、偶然か(conflict)か、 区別する手段がないところです(名称重複問題)。
もうひとつは、いくつかの状況で継承ラインに並ぶモジュールの順序が予測しがたい (ので、メソッド名の重複時にどれが優先になるのか直感的でない)ことです。
module American attr_accessor :address end module Japanese attr_accessor :address end class JapaneseAmerican include American include Japanese end JapaneseAmerican.new.address # which address? p JapaneseAmerican.ancestors # => [JapaneseAmerican, Japanese, American, Object, Kernel]
この例ではaddressという属性(メソッド)がAmericanとJapaneseの間で 重複していますが、これが意図的な重複なのか偶然かは言語にはわかりません。 継承ラインの順にしたがってメソッドを呼び出すだけです。
実際にはJapaneseモジュールが優先されてそのaddressメソッドが呼ばれるのですが、 ひとめでそれが分かるのは、だいぶ「訓練されたRubyist」です。
現在のRubyでは、includeされた時、 「スーパークラスですでにそのモジュールがincludeされていた時には 二重にincludeしない」という挙動になっています*1。ですから、 スーパークラスでincludeされていることに気がつかなかった場合、 includeしても継承ラインのその場所にモジュールが登場しなかった ということが起こりえます。
それから、モジュールが既にincludeされてから、 そのモジュールに対してincludeを行った場合、 既に存在するクラスの継承ラインには新たにincludeされるようになったモジュールは含まれません。 つまり、includeのタイミングによって継承ラインへの反映のされ方が異なるわけです。 ちょっと気持ち悪いです。
これらを(ある程度)解決する手段がmixメソッドです。
mixメソッドをincludeの代わりに使うと、
という振舞いになります。これにより
ということが実現できます。
たとえば以下のようなコードでは
module American attr_accessor :address end module Japanese attr_accessor :address end class JapaneseAmerican mix American mix Japanese # => address conflict! end
addressメソッドが重なっているからmixできません。 無事mixさせるためには名称衝突を明示的に回避します。
class JapaneseAmerican mix American, :address => :us_address mix Japanese, :address => :jp_address end
これで、addressという名前による重複はなくなりました。
なぜ、includeにオプションをつけるのではなく、 新しいメソッドを導入して言語をより複雑にするかというと、 個人的にmixの挙動の方が望ましいと思っていて、 ユーザーをそちらに誘導するためには、より短い名前の方が望ましいと思ったからです。
Traitsを実現するmixメソッドの実装ですが、 RubyKaigiでこれを紹介したその日には中田さんが着手していて、 パッチは完成しているそうです。
ただ、各種プレゼンテーションでは説明しなかった以下の課題があり、 これらについては結論を出す必要があります。
RubyKaigiではmixの一部として導入する話をしていたMethod Combinationだが、 mixでいちいち「どのメソッドをラップするか」とか指定するのが以上にめんどくさいことに 後で気がつきました。ので、分離。
今回の案はprependというメソッドを導入すること。「include、mixに続いて またもうひとつ?」という声が聞こえてきそうだが、私もそう思います。でも必要なのよ。
prependはそのモジュールが提供する機能を、現在のクラス/モジュールの「前」に 追加する機能。
module Foo def foo p :before super p :after end end class Bar def foo p :foo end prepend Foo end Bar.new.foo # :before, :foo, :after
とように使う。prependしたモジュールFooで定義されたfooメソッドが、 prepend先のメソッドfooをラップしているのが分かるでしょうか。
prependメソッドは、RailsコミッタでもあるYehuda Katzの提案で、 これがあればRailsのalias_method_chainを撲滅できる、と息巻いていた。 私もそう思う。
具体的な実装はまだないんだけど、たぶんT_ICLASSのようなものを 継承チェーンに置いて、そっちを先に検索するようにするんじゃないかなあ。
引数、特にオプショナル引数がどんな働きをするのか忘れる人は私だけじゃないと思います。 たとえば、 public_instance_methods メソッドはオプショナル引数を受け付けるのだけど、 それが「オプショナル引数を付けると、それが真であった時にスーパークラスのメソッドを含む」のか、 それとも逆かというのは私でもいつも忘れてしまいます。正解はfalseを付けた時に含まない。
これをたとえば
aClass.public_instance_methods(include_super: false)
と書けたら、ずっと覚えやすくなるというものです。
Rubyのキーワード引数は、1.9で追加されたシンボル記法のハッシュが 引数リストの末尾に付いているだけです。
2.0で新たに追加されたのは、メソッド定義側でこれを簡単に受け取れる記法です。
例としてはこんな感じ。
呼び出し側
1.step(by: 2, to: 20) do |i| p i end
呼び出され側
def step(by: step, to: limit) ... end
後、「**」で辞書形式で受けとるとか、ちょっとした機能追加もありますが、 基本的にはこれだけ。
技術的な詳細などについては同じRubyConfで前田(修吾)くんが発表したスライドを見てもらった方が良いと思います。
Rubyではopen classといって既存のクラスの定義を書き換えちゃうことができる。 メソッドの追加も自由自在だ。このように既存のクラスに「パッチ」を当てちゃう技法のことを 「Monkey Patching」と呼ぶことも多い。
これは「ゲリラ・パッチング」→「ゴリラ・パッチング」→「モンキー・パッチング」と 変化して生まれた用語なんだって。 まあ、Rubyはクラスなんてものは変化するもんじゃないって「硬い」言語よりも 大きな自由を提供してることは確かだよね。 DHHは今回のRubyConfのキーノートで「今後はMonkey PatchingじゃなくてFreedom Patchingと呼ぼう」と 叫んでた。メル・ギブソンの『ブレーヴハート』を引用しつつ。「ふりーだーーむ」。
まあ、フリーダムなのは素晴らしいことなんだけど、影響力が大きすぎるというのもまた事実。 やろうと思えば整数のプラスメソッドを書き換えて、1+2 = 42 のような変更だってできちゃうから。 でも、大抵のプログラムは副作用でまともに動作しなくなるよね。
で、問題はこのような変更の影響の範囲がグローバル(プログラム全体)であることで、 仮にこのような修正をなんらかの「スコープ」に閉じ込めることができたなら、 もっと安全に、もっと安心して「フリーダム・パッチング」を活用できる、はず。
そのような「スコープ」のために、昔からClassboxとかSelector Namespaceとかが提案されてきた のですが、今回、前田くんが実装したのはSelector Namespaceの一種であるRefinment。
たとえば、以下のようなプログラムがあったとします。っていうか、あります。
class Integer def /(other) return quo(other) end end p 1/2 # => (1/2)
これは割り算演算子(/)を再定義して、整除ではなく結果を有理数にしようもので、 標準添付ライブラリの mathn の本質部分です。 しかし、整数の割り算の結果が整数であることを期待しているコードは当然存在するわけで、 そのようなコードは上のような変更で破綻する可能性があります。
そこで今回導入しようというのがrefinmentです(呼び名は変わるかもしれません)。 文法としては以下のようになります。
module MathN refine Integer do def /(other) return quo(other) end end p 1/2 # => (1/2) end p 1/2 # => 0
Refinementの単位としてはモジュールを使います。またモジュールです。大活躍ですね。
モジュールの中では既存のクラスをrefineできます。 refineの中で定義された修正はそのrefinment(ネームスペース)の中でだけ有効です。 ですから、MathNモジュールの中では 1/2 は有理数の (1/2) であり、 その外側では今まで通り整除になっています。有効範囲はレキシカルであり、 Refinmentはブロックの外側には影響を与えません。
Classboxとの違いは、そこを通じて呼び出された先(レキシカルスコープの外)に 「置き換え」が影響を与えるかで、いろいろ検討した結果、 多くのプログラミング言語がダイナミックスコープをあきらめたのと同様の理由で 「置き換え」はレキシカルになるべきだとの結論を出しました。
モジュールとして実現されたネームスペースを使うには usingメソッドを使います。 こんな感じ。
module Rationalize using MathN p 1/2 # => (1/2) end p 1/2 # => 0
これでRationalizeモジュールの中ではMathNが提供するRefinementが利用できます。
さらに、今までメソッドの中でメソッドをネストして定義した場合、 そのメソッドはクラスに直接定義されてあんまり意味ないじゃん、みたいな状態になっていたのですが、 今後はそのメソッドの範囲内でだけ有効なRefinementにネストの内側のメソッドが定義されるので、 完全にプライベートなメソッドとして使うことができます。
class Foo def foo def bar ... end bar # 呼べる end def quux bar # 呼べない end end
この変更はかなり大規模かつ複雑なものですが、前田くんのところでは実際に動作しています。 早く trunk に突っ込みたいものです。しかし、NaCl取締役の激務をこなしつつ、 こんなスーパーなパッチを作っちゃう前田くんに拍手。
歴史編で見てきた通り、ずっと昔からキーワード引数などについて話してきましたが、 とうとう現実になりそうです。長かった。
*1 MacRubyでは違うらしい。Ruby 1.9でそのような変更をしたかったが、YARVが継承ラインに同じモジュールが2度登場しないことを前提にしていたため断念
New Orleansで開催されているRubyConf 2010に来ています(執筆時点では既に終了)。
で、今回のRubyConfはキーノートが3つもあるという豪華版でした。 ひとつは1日目最初のDave Thomasによるもの、 次は2日目最初のDavid Heinemeier Hanssenによるもの、 最後が2日目最後の私によるものです。
他のキーノートは、それぞれ感動的で素晴らしかったのですが、 その辺は他の方のレポートにお任せしようと思います。 そのうち、confreaksからビデオも公開されるだろうし。
で、今回は私のキーノートで発表した内容の紹介と その解説をしてみようと思います。
ちなみにスライドはSlideShareにアップしました。
今回のテーマは
でした。Matzにっきでは、これを3つに分けて紹介しようと思います。
今日はまず最初の「History」について。
RubyConfはもう今回で10回目になります。 最初は30人強で始まったRubyConfも、今回は800人を越える規模に成長しました。 っていうか、ここ数年は会場の都合などで500人規模で安定していたのですが、 あっという間にチケットが売り切れるありさまでした。昨年など24時間保たなかったようで、 いけるかな、などと悩んでいるともうダメという状況です。
今回は会場が広かったこともあって、チケットにもだいぶ余裕があったようです。
過去のRubyConfとそこでのキーノートを振り返ると、
記念すべき最初のRubyConfです。ACMのカンファレンス、OOPSLAの直前を狙って開催されました。 日本人は3名(私、高橋さん、青木さん)。この年は 911 の年でもあり、 また、フロリダは炭疽菌の騒ぎが起きた場所だったので、まわりに大変心配されました。 テレビのニュースではAnthrax(炭疽菌)という単語が繰り返し登場していました。
あと、フロリダでは父の古くからの知人のお宅にお邪魔したりして、 久しぶり(23年ぶり)の米国訪問は楽しい時間になりました。
この年のキーノートのタイトルは「Human-Oriented Programming in Ruby」で、 Rubyは人間にフォーカスしてるんだよ、みたいな話をしました。原点ですね。
2回目のRubyConfはSeattle.rbのお膝元シアトルで開催されました。 この時もOOPSLA直前開催です。 Seattle.rbの面々は、Aaron Pattersonはいなかったと思うけど、Eric HodelもRyan Davisも すでに参加していました。Dan SugalskiがParrotの話をしたのもこの回だったような。
キーノートは「Be Minor, Be Cool」です。 クールなことはマイノリティからくる。 我々は(まだ)あまり知られていないかもしれないけどクールなことをしようじゃないか という呼びかけです。
この時も(この回まで)OOPSLA直前だったような気がします。 思い出されるのは、RubyConf前に Texus A&M Universityを訪問して Bjarne Stroustrupと時間を過ごしたことです。 個人的に私が C++ についてどう思っているか、ということは置いておいて Bjarneは素晴らしい人格と素晴らしい頭脳の持ち主です。とっても親切でした。
この時のキーノートは「Visions for the Future, or How Ruby Sucks」です。 Ruby 2.0がどうなるか、なんて話をこんなに前からしてたんですね。 M17Nや鬼車、あと定数の検索順序のようなその後1.9で実現されたものもあれば、 ローカル変数のスコープのように、悩んだ挙句結局変更しないことにしたものもあり、 キーワード引数やSelector Namespaceのように、やっと実現のめどがついたものなどが もう7年も前にそうとう詳しく述べられています。
ここで紹介されたアイディアの中にはprivate instance variable(サブクラスから見えないインスタンス変数)のような、秀逸ながら文法が決まらずお蔵入りのものもありました。手元にはパッチもあるんですよねえ。
Chantillyは「シャンティ」と発音するようですね。
実はこの回は出席できていないんです。ちょうど末娘の誕生のころで、 さすがに臨月の妻をおいてアメリカへ行くのははばかられました。 この回は、笹田くんがYARVの発表をしています。 非常に好評だったと聞いています。キーノートはObjecive-Cの作者 Brad Cox が してくださったそうです。もっとも Objective-C については全く言及しなかったようですが。
サンディエゴはあたたかくていいところでした。 ホテルもおしゃれでした。向こうでは、偶然知人のお父さんに会って ディナーをご馳走になったりしました。
この年のキーノートは 「Visions for the Future, or Wild and Weird Ideas」で、 やっぱり未来に向けての「ワイルドなアイディア」について語っています。 keyword arguments, eval, lambda, annotations, traits, namespace, method combination, multilingualization などについて語っていますが、 こんなに昔からいろいろ考えていたのねと感心するべきか、 こんなに時間がたってもぜんぜん手付かずのものがこんなにあるのねとあきれるべきか。
後者なんでしょうね。
なんかデンバーとっても寒かったです。会期中には雪が降ったし。 ホテルの駐車場でリスがうろうろしていたのが面白かったです。
この時のキーノートは「The Return of the Bikeshed or Nuclear Plant in the Backyard」 というテーマでした。言語設計はとっても楽しいからみんなも参加しようよ、 というような感じです。その後、いろんな人が参加してくれるかなと思ったのですが、 実際にはいまいち盛り上がりませんでした。やっぱ言語設計は敷居が高いんでしょうか。
東海岸です。2004年のバージニアを逃した私には、はじめての東海岸らしい東海岸です。 分類上は2001年のフロリダも東海岸ですが、東というよりも南という印象が強いですね。
この年は「Language Matters or Not」。プログラミングにおいて言語の果たす役割は大きい、 とかなんとか。あと恒例の新機能紹介も入ってます。 その後激論を呼び起こした「->(Stabby Lambda)」が公表されたのもこの時でしたね。 あの時はむしろ反対が多かったStabby Lambdaですが、みんなあきらめたのか 最近では文句をいう人はもうあまりいませんね。計算通り。
2度目のフロリダ州です。ディズニーワールドなどリゾート地の印象が強いオーランドですが、 ホテルはかなり世間と隔絶したところにあったので、集中できました。 ちょうど大統領選挙の激戦の最中だったので、「どっちを支持する」とか 真剣に聞かれて困りました。ところでプログラミング関係はリベラルで民主党支持の人が多いようですね。
この年のキーノートは「Reasons behind Ruby」です。 わりと原点に戻ったキーノートですね。Rails世代が増えてきてることを意識したキーノートです。 技術色が薄いので、あんまりおもしろくないと思った人も多かったかもしれません。
2009年RubyConfの会場は、シリコンバレーにほど近い、IT業界の中心地サンフランシスコです。 なんかRubyConfの前の日にInfoQが開催するQConでも セッションを持ったりして忙しかった覚えがあります。
この年のキーノートは「The 0.8 True Language」です。 聞き慣れない表現ですが、指輪物語の「One True Ring」を意識しています。 すべてに向いた「One True Language」はなくとも パレートの法則に従い80%をカバーする言語はありえるんじゃないか、 Rubyはそれに近いんじゃないか、とかいうような話です。
今年はニューオーリンズです。実はニューオーリンズは松江市の姉妹都市なんですね。 だからどうということはありませんが、音楽が鳴り響く明るい感じの街で、 食べ物がおいしいのが素晴らしいです。カンファレンスご飯もおいしくて 参加者達がびっくりしていました(大抵は、食べれるけどそんなにおいしくない)。
そして今年のキーノートのテーマは「Future and Diversity」です。
続きは別エントリで。まず Future (≒Ruby 2.0) について、 そしてその次のエントリで Diversity (多様性) について解説します。