ホテルで朝食をすませた後、Stroustrup教授の車で大学まで。 大学を一周して見物させてもらう。
しかし、広い大学だ。筑波大もいいかげん大きいなあと思っていたけど、 ここはその何倍も広い。冗談のような大きさのスタジアムが印象的だった。
Bjarneいわく「キリスト教はテキサス州2番目の宗教だ。最大はフットボール」だそうで、 熱の入れ方は半端ではないそうだ。
午前中はBjarneの紹介で、Salih Yurttas教授と会う。 「今度は授業でPythonを扱おうと思ったけど、Rubyにしようかなあ」とおっしゃっていた。 言語好き同士話が弾む。
昼食をBjane、Salihと一緒にとった後、今度はComputer Science Dapartmentのディレクター、 Valerie Taylor教授に紹介される。
しかし、いくらわざわざ海外から来た客だとはいえ、 実質的に無名な私にきちんと時間を割いてくれる丁寧な扱いに感銘を受ける。 しかも、コンピュータ科学部のボスまで。感謝。
Valerieは
私はずっとAWKなの。 研究してるシステムはPerlを使ってるけど、開発は学生がやってるから。
とのこと。
今回は夕べのディナー、今日の朝食、昼食とずっとBjarneと一緒だった。 せっかくの機会なので聞いた話をまとめておこう。思い出せることを端から書き上げたので、 順不同、時系列無視なのであしからず。
Matz(以下M): C++にはSimulaの影響が見えますけど、他のオブジェクト指向言語、 たとえばSmalltalkからは影響を全然受けていないんですか?
Bjarne(以下B): 受けてないね。 私は元々(ケンブリッジ大の大学院で)分散システムの研究をしてたんだけど、 分散システムのシミュレータを書くのにSimulaを使ったんだ。 ところがSimulaは遅すぎて結局BCPLで書き直したんだけどね。 こういう経緯があるんでCにSimulaの機能を追加したC with Classを作り、 最終的にはC++になったというわけなんだ。
M: Javaについてどう思いますか? (Aarhusでも聞いたけど)
B: 聞かないでくれ。もしPositiveなことを言えば、そんなので満足してるかって言われるし、 Nagativeなことを言えば波乱が起きるし。 15年くらい前にはObjective-Cの連中といろいろあったし。 言語論争は嫌いだ。
M: そのObjective-CのBrad Coxが今JavaとRubyを使ってるっていうんですから皮肉ですよね。 今なにを研究してるんですか。
B: 今は他のプロジェクトを助けたり、会議したりしてるんだ。院生を指導したりもしているよ。 こっち来てから1年くらいにしかならないんで本格的な活動はまだだね。 また分散の研究をしようかなあって思ってるんだ。 あと、大学っていうのは研究だけじゃなく教育もやるところだからね。それについても活動しているよ。
M: というと?
B: 来年そうそうから学部生の授業を持つことになってるんだ。C++を教えるんだけど。 で、いい教科書がなくてねえ。困ってるんだ。 C++を使った教科書はどれも分厚いばっかりで面白くないし。 たとえばこれなんか「回文チェック」って例題のために、 擬似コードでの回文とはなにかって説明に2ページ、C++のプログラムにまた2ページ使ってるんだよ。 テンプレートを使えばこんなのワンライナーなのに(と黒板に書きはじめる)。 おまけにこれなら任意の型のベクターの回文チェックができる。
B: 今は学生のためにGUI機能を設計してるんだ。 今時の授業じゃGUIを使わないわけにはいかないけど、 私が授業でやりたいのはツールキットの説明じゃない。 既存のツールキットをそのまま使ったらその説明時間だけで授業が終わっちゃうからね。 ダイアログを呼び出す同期呼び出しとポーリングを使った非同期呼び出しができる単純なものを考えてるんだ。 やっぱり複雑過ぎるものは駄目だからね(と、設計を黒板に書きはじめる)。
他にもいろいろと話したような気がするのだが、忘れてしまった。もったいない。 録音でもしておくべきだったか。大半はアメリカ、日本、デンマーク、トルコ(Salih教授の出身地)の 文化・慣習の違いについてとかだったような気がするけど。
0201543303 しかし、Bjarneはえらく頭が良くて、しかも(私の視点から見ても)言語設計に対して「正しい思想」を 持っているようなのに、C++がえらくアレなのはいったいどういうことだろうか。 やはりC++が志向する領域が本質的に複雑で、その複雑な状況に対応するためには言語も複雑にならざるをえなかったということなのだろうか。 もちろんC++の問題の本質は使い方によるのであるが、そういう使い方ができてしまうのも問題といえば問題なのだが。
0201543303を(サイン入りで)いただいたので、 ちょっと研究してみることにしよう。
夕方からは今回の招待のメイン、招待セミナー。 今回のスライドはここ。 下手な英語ではあったが、そんなに悪い話はしなかったと思うけど、あんまり盛り上がらなかったなあ。
その後、Austinに移動。運転手は昨日と同じ女性だ。今夜は1時間半ほどで到着。 やっぱり途中は寝ていた。乗り物に乗るとすぐ寝ちゃうなあ。
ホテルでRich Kilmer、Chad Fowler、David Alan Blackたちに会う。 少しおしゃべりしてから部屋へ。
で、部屋でLinux Magazineの原稿を書くのであった。
追記:
ツッコミをいただいたので返事しておく。
まず、KLさん。Rubyについて聞かなかったのは、 彼がRubyについて名前以外なんにも知らなかったからです。 私がセミナーで説明した後ではある程度分かって「面白い」と言ってくれましたが、 その後すぐ別れちゃったし。つっこんで聞く機会はありませんでした。
次に、とおりすがりさん。 「私がJavaが嫌いだからBjarneからネガティブな発言を引き出したがっている」という推理は 当たっていないように思います。だって嫌いじゃないもん。 興味が持てないだけで。たぶんBjarneも同じ意見だと思います。
小学校1年生の息子は最近まで自転車にさっぱり興味がなかったのだが、 とうとう自転車に乗ることを覚える気になったらしい。
「やる気になった時にやらせる」のはうちの教育方針なので、 さっそく自転車を買いに行くことにする。 自分で自分の自転車を決めることそのものが、 脳味噌の普段使っていない部分を刺激するらしく、頭から煙が出そうなくらい悩んでいた。
あちこちの店を回ってさんざん悩んだ揚げ句、 BMX風の22インチ車を購入することにした。普通、もうちょっと子供っぽいのにするんじゃないのか。
まあ、いいか。
司会。あまりうわずらなくなってきた。
副監督の一人は岡山、監督も聖餐会終了後は監督訓練集会で岡山。 取り残された私は少々不安だったが、とくになにごともなく終了。
集会終了後、面接。いろいろと懸念事項を聞く。 教会といえどもいろんな人の集まりなので、 心配事やら、問題やらいろいろあるものだ。
依頼されたことは手配しておく。
「オープンクラス」完了。疲れた。
家庭の夕べで緊急持ち出しカバンの中身をチェック。
前回チェックしてから数ヶ月にしかならないのに、 虫がついてたり、消費期限が過ぎたりで、だめになってるものがあったり。 なかなかメンテナンスが面倒なものだ。
QWERTYに似てるけど、ずっと良いキー配列。
なんだそうだ。具体的には以下のようになってる。
TAB Q W F P G J L U Y ; [ ] \ BS A R S T D H N E I O ' SHIFT Z X C V B K M , . /
個人的には大量に文書を入力する日本語に対して「きゅうり改」があるから、 そんなに魅力的ではないけど、英文をたくさん入力する人には効果的、かも。
もう知っている人は知っているGoogleからのシステムプログラミング新言語Go。
すっかり祭りには乗り遅れた感があるけど、少しだけコメントをつけておこう。
総合的に見て、非常にバランスを考えているように思える。 JavaやC++のような複雑さを排して、シンプルに徹する一方、 言語好きを刺激するような新しいアイディアをそこかしこに配置している。
特徴である「コンパイルが高速」というのも、 このシンプルな言語仕様が寄与していると思われる。
個人的に、注目したのはオブジェクト指向機能と、並列機能。
interfaceによる、継承のないオブジェクト指向(duck typing)は、かなり私好みである。 昔からこういう言語が欲しかった。interfaceのみ動的結合を許すというのも Satherを思い起こさせて好印象。
ただし、私が見た範囲内では、実装の継承(共有)は提供されないみたいなので、 Javaのinterface同様、コンポジションを強制されることになるので、 そこは残念。
それから、goroutineなる並列実行機能。 Erlangを意識してるのかな。でも、それ以上に使いやすそうな印象。
go 式
だけで並列実行が開始できるのは大胆である。 ほとんどのオブジェクトが値渡し(コピーされる)とあいまって、 効率よく並列実行できそう。
あと、多値を活用しているもの面白い。 成功したかどうかを2番目の値で返すところとかもErlangを思い起こさせる。
「逆転した型宣言」は、はじめはギョッとしたけど、 慣れたら結構良いかもしれない。
コマンド名! 8cとかなんだよって感じ。
ソースコードを眺めたが、正直、一番興味があるランタイムの部分は よくわからなかった。だいぶ慣れないとなにがどこにあるのかよくわからない。
GCはシンプルなマーク・スイープだった。
goroutineのsegmented stackの実装に興味があったのだが、 まだみつからず。たぶん、そこはランタイムじゃなくて、 コンパイラそのものを見ないとわかんないんだろうな。
「Goが登場したからには(Java/C++/Python)はお払い箱」みたいな エントリをいくつか見かけたけど、まだまだそういうレベルではないと思う。
Goは非常に興味深い言語だし、当面いろいろと話題を提供してくれるとは思うけど、 これが定着するか、成功した言語になるかどうかは、長い目で見ないとわからないと思う。
GoogleやRob PikeやKen Thompsonのネームバリューのおかげで、 普通なら10年かかるプログラミング言語のブランド確立を一瞬で達成してしまった Goだけど、成熟度としてはまだまだなので、当分はそのギャップに苦しむ(?)ことに なるんじゃないかな。
そこを乗り越えて、「一人前の言語」になっていくことを期待したい。
前回に続いて 未来(≒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度登場しないことを前提にしていたため断念