Rubyist のための他言語探訪 【第 3 回】 Io

著者:まつもとゆきひろ

編集:なかむら

はじめに

「Rubyist のための他言語探訪」は、Ruby 作者まつもとゆきひろが、Ruby と関係があったりなかったりする他の言語を紹介していく連載です。 他の言語を知ることによって、視野が広がると同時に、逆に Ruby への理解も深まることでしょう。

今回は、Io を紹介します。

Io について

本連載の 1 回目では Python、2 回目では CLU と、Ruby よりも古く、デザインの参考にさせていただいた言語を取り上げました。 第 3 回目となる今回は、うってかわって Ruby よりもずいぶん新しい言語を取り上げます。 今回のお題は「Io」です。

Io は Steve Dekorte によって開発されたプロトタイプベースのオブジェクト指向言語です。 ドキュメントによれば、Io が誕生したのは 2002 年 4 月だそうです。 Io は「あい・おー」と発音します。

Io の特徴

Io の特徴はなんといってもその単純さです。 言語のわかりやすさという観点から考えると、単純さはかならずしも良いこととは限りま せんが、いずれにしても Io の単純さは徹底しています。 また、ドキュメントによれば実行もなかなか高速で、他のアプリケーションへの組み込みも考慮しているとのことです。 ただし、本連載の主眼は言語仕様にありますから、このあたりには触れません。 御自分で公式サイトからダウンロードするなどして確かめてください。 対応しているプラットフォームも、OSX, Linux, FreeBSD, NetBSD, Irix, Solaris, Windows, Symbian, Syllable となかなか多彩です。 処理系のソースコードは BSD ライセンスで提供されています。

プロトタイプベース

プロトタイプベースとは、新しいオブジェクトを作り出すのに既存のオブジェクトをコピーすることを基本としたオブジェクト指向プログラミング手法です。 それに対して、Ruby を含めた多くのオブジェクト指向言語が採用している、クラスからそれに属するインスタンスを作り出す方法はクラスベースと呼ばれます。

プロトタイプベースのオブジェクト指向言語として有名なものには Smalltalk そっくりな文法の Self という言語があります。 が、Self が起源というわけではないようです。 聞くところによると、HP の Snyder が開発した CommonObjects という Lisp 上のオブジェクトシステムでは「クラスとオブジェクトに違いがなく、サブクラスを作ることと、インスタンスを作ることが(操作の名前は違うものの)等価である」ものだったそうなので、これがプロトタイプベースでもっとも古いものではないかと推測しています。 ただ、Self と CommonObjects 双方の開発が始まった正確な時期を確認することができなかったので、単なる憶測に過ぎないかもしれません。

プロトタイプベースオブジェクト指向プログラミングは Ruby でも特異メソッドを駆使して真似事を行うことはできます。 しかし、Ruby では整数やシンボルなど一部のオブジェクトに特異メソッドを付与することができないので、あまり現実的ではありません。

Io でのオブジェクト指向プログラミングは以下のようになります。

 // Object をコピーする。
 Dog := Object clone  // Dog はクラスとして振る舞う
 // 雛型 Dog に sit というメソッドを教える
 Dog sit := method("I'm sitting\n" print)

 // Dog は犬なので sit できる(クラスメソッドはない)
 Dog sit

 // 雛型 Dog から新しい myDog を作る
 myDog := Dog clone
 // myDog に名前を付ける
 myDog name := "rover"

大体感じが掴めるでしょうか。 プロトタイプベースとすることで

  • クラスを定義する構文が要らない
  • メソッドを定義する構文が要らない

など文法が非常に単純化されています。

メッセージ指向

Io の徹底したところはまだ続きます。 Io ではすべてがメッセージセンド(メソッド呼び出し)で実現されています。 Ruby も大概のものがメソッド呼び出しですが、Io ははるかに徹底しています。

Io のメッセージ式の文法は以下の通りです

 <message name> (<arg1>, <arg2>, ...) <attached message, new line or semicolon>

セミコロンか改行が来るとメッセージ連鎖は終了します。 メッセージが続くときにはいつも左側のメッセージセンドの結果が右側のメッセージセンドのターゲットになります。 また、引数がないときには括弧は省略できます。 ですから、

 Dog sit

は、より正確に書けば

 Dog() sit()

で、その意味は「Dog というメッセージセンドの結果に sit というメッセージを送る」です。 最初のメッセージはそのスコープの名前空間オブジェクトになります。

Ruby 同様、Io でも演算子はメソッド呼び出しです。 Smalltalk では二項演算子に優先順位がありませんが、Io では「普通の」優先順位が適用されます。 ですから、

 1 + 2 * 3

 1 + (2 * 3)

と解釈されます。

なにもかも徹底している Io では、代入さえもメッセージ式です。 これは Smalltalk を凌駕する徹底度です。 Io の代入の文法は以下の通りです。

新しい変数(スロット)を作る場合

 <slot name> := <expression>

既存のスロットを更新する場合

 <slot name> = <expression>

これらは実際には以下のように解釈されます。

 <slot name> := <expression>
 setSlot("<slot name>", <expression>)
 <slot name> = <expression>
 updateSlot("<slot name>", <expression>)

制御構造

大抵の言語には予約語というものが存在しています。 それらの言語では制御構造や定義文などは予約語で指示されます。 しかし、Io には予約語というものが存在しません。 これは Lisp を思い起こさせますが、Lisp でもスペシャルフォームは予約語のように扱われますから、もっと徹底していると言えるかもしれません。

Io では制御構造もメッセージ式です。 たとえば if 文は以下のようになります。

 if(y < 10, x := y, x := 0)

メッセージ式の形をしてはいますが、意味は明確ですね。 これを実行すると期待通り「y が 10 より小さければスロット x に y を代入、そうでなければ x に 0 を代入」と動作します。 複数の式を指定したい場合には、改行又はセミコロンで区切ってメッセージ式を並べます。

 while(a < 10,
   a print
   a = a + 1
 )

こんな感じです。

メソッド定義

メソッド定義は method メッセージを使ってメソッドオブジェクトを作り、それをスロットに代入することで行います。 method メッセージは、末尾の引数をメソッド本体、それより前の引数をパラメータ名として受け取り、メソッドの名前空間に代入します。 古典的な階乗を定義してみましょう。

 fact := method(n, if(n==0,1,n*fact(n-1)))

まあ見掛けはともかくなにも変なことはありません。 しかし、良く考えると少々不思議なことがあります。 Io は全てがメッセージ式であるので if や while のような制御構造なども含めてメッセージセンドで実現されています。 しかし、普通の言語ではメソッド呼び出しの時点で引数を評価してしまうので、制御構造を実現できません。 たとえば Ruby で

 def myIf(cond, x, y)
   if cond
     x
   else
     y
   end
 end

のようなメソッドを定義しても

 myIf(y < 10, x = y, x = 0)

は期待通りに動きません。 全ての引数を条件判断より前に実行してしまうからです。

実は Io の引数渡しには秘密があります。 Io は「メッセージセンドの時点では引数を評価しないで渡す」のです。 method で作られたメソッドオブジェクトは呼び出されたときに、呼出元のコンテキストで引数を評価してローカル名前空間のパラメータに代入しているのです。 評価せずに引数を取り出すためには thisMessage argAt(n) を使います。 実例として自前の if 文を定義してみましょう。

 myIf := method(c,
   if(c, sender doMessage(thisMessage argAt(1)),
         sender doMessage(thisMessage argAt(2)))
 )

sender が呼出元の名前空間、doMessage がメッセージ式を評価するメソッドです。

 myIf(1<2, "foo" print, "bar" print)

を実行してみると foo だけが出力され、正しく if 文として動作することが分かります。

このように Io は単純な仕掛けで大きな拡張性があることが分かります。

まとめ

今回は解説しませんでしたが、Io はこの調子で多重継承(相当)なども実現しています。 なかなか柔軟で優れた点の多い言語だと思います。

では、この言語を使って日常的にプログラムしたいかというとやや疑問が残ります。 小さなプログラムを作って「こんなこともできるんだ」という驚きを感じるにはもってこいの言語ですが、ある程度規模の大きなプログラムや、複雑なプログラムでどうなるかについてはかなり好みが別れるところではないでしょうか。

Io は日本でも注目されていて、日本語ドキュメントIo を積極的に取り上げるブログなども登場しています。 技術的な興味からだけでも眺めてみて後悔しない言語だと思います。

参考文献

io
Io の公式サイト。ソースコードの配布、各プラットフォーム用バイナリへのリンク、ドキュメント、などがある。
Io 文書
Io ドキュメントの日本語訳。

著者について

elephant_at_hokudai.jpgまつもとゆきひろは自他ともに認める日本を代表する言語オタクです。 言語好きが昂じて自分の言語を設計してしまった大馬鹿者です。 が、オタクとかハッカーとか呼ばれる人種はみんな多かれ少なかれそんなものじゃないでしょうか。

Rubyist のための他言語探訪 連載一覧