で、最後に「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 (多様性) について解説します。
上海の電気街(っていうか巨大なラジオ会館)のようなところで買い物。
携帯電話がカオス。Suny EricssonとかNCKIAとかいっぱいある。 あと、大量のiPhone風電話とか。 Androidで動くiPad風タブレットもあった。1500元。なぜかメニューが全部日本語。
帰りに容量500Gと書いてあるUSBメモリ(Sonyブランド)があったので、 値段を聞いたら100元(約1500円)だとか。 安すぎて、絶対にその容量はなさそうだけど、 話のタネにと思って購入。ねぎったら80元になった(!)。
ホテルに戻ってPCにつないだら、本当に524Gと表示された。びっくり。 この容量がぜんぶ使えるんだろうか。いろいろ試してみないと。
やっぱ、使えなさそう。
しかし、こんな使い物にならないものを、誰がなんのために作るのか。
ネタになったからいいけど。
注意。このエントリはRuby 2.0に入るかもしれない機能について述べていますが、本機能が本当に2.0に採用されるかどうかは、未定です。
「まだあるのか」とか思われそうだけど、まだあるんです。
今度は長年採用すると言いつつ、ほったらかしの最右翼、キーワード引数である。 調べたら、キーワード引数については、2003年のRubyConfではすでに話題にしている。 どんだけ前から口にしてたんだ。「やるやる詐欺」だな(苦笑)。
その時の仕様はこんな感じ。
def foo(a, b: 42, **keys) p [a,b,keys] end foo(1) # => [1,42,{}] foo(2, b: 5) # => [2,5,{}] foo(3, b: 4, c: 6) # => [3,4,{:c=>6}]
まあ、そんなに悪くない。基本的にはこの線を踏襲しようと思う。 文法としては、ブロック引数の直前に
keyword: value,..., **keys
というキーワード引数を置くことができる、というもの。 Pythonと違ってキーワード引数は明示的に指定しなければならない。 通常の引数名が勝手にキーワードになったりはしない。
問題はrest引数と組み合わさった時。
def baz(*rest, a:4, b:0, **keys) ... end
として、以下の呼び出しを行ったらどうなるか。
baz() # rest=[],a=4,b=0,keys={} baz(1) # rest=[1],a=4,b=0,keys={} baz(a:1) # rest=[],a=1,b=0,keys={} baz(a:1,b:2) # rest=[],a=1,b=2,keys={} baz(1,2,b:2) # rest=[1,2],a=4,b=2,keys={} baz(c:2) # rest=[],a=4,b=0,keys={c:2}
つまり、以下のルールに従う。
なお、※の部分はRubyConf 2005のキーノートで紹介したものと動作が違う。
この仕様ではキーワード指定はあくまでも通常引数の一部なので、 通常引数の引渡の時には *rest で受けて、 foo(*rest) と呼び出せばよい(はず)。
呼び出し側のキーワード引数は結局は引数リスト末尾のハッシュに過ぎないので、 呼び出し側では foo(**keys) のような文法は(本来は)不要。 ただし、対称性と型チェックのために採用する可能性は、なきにしもあらず(あんまり積極的ではない)。