局所変数無き荒野を生きる

古の超最先端言語である Self の際立つ特徴は二つにまとめられます。

Self はしばしばプロトタイプベースのオブジェクト指向計算機言語の代表格に挙げられますが、そのもう一つの重要な側面である局所変数がないという特徴にはあまり注目されません。実際のところ大域変数すらあるわけではなく、Self においては「変数」という言葉は全てオブジェクトの「スロット」によって置き換えられていると考えて良いのです。おお、なんてこった!最先端過ぎてついて行けないよ!

"こういうのは全部実はメッセージでスロットの値を得ているだけ"

traits oddball
"オブジェクト shell のスロット traits の値であるオブジェクトの"
"スロット oddball の値を取得"
"大域変数がないからスロットを使ってるってワケ!"

(| x: = (|arg:| arg + y). y = 1 |)
"メソッド x: における arg だって"
"暗黙のメソッドオブジェクト self にメッセージ arg を送ってます"
"だから局所変数じゃないよ!"

[|:x| x]
"このブロックにおける x は"
"暗黙のブロックオブジェクト self にメッセージ x を送ってます"
"だから局所変数じゃないよ!"

メソッド内における暗黙の self がメソッドオブジェクトだったら、コード arg + y における y のレシーバも同様にメソッドオブジェクトになるから困らない?と不思議に思うかも知れませんが、メソッドオブジェクトは parent slot として self* というのを持っていて、その値がメソッド所有者のオブジェクトになっているから delegation でうまいこと y の値を取得できます(詳細は Self Reference Manual の 2.2.3 Slots containing methods をご覧下さい)。とっても賢いですね、これぞまさにプロトタイプベースがこそ!

メソッドの中では暗黙の self がメソッドオブジェクトになっていることはとても分かりづらいですよね。

inspect: (| x = (self) |) x
"=> ( | x = ( self). | )"

以上のように、(| x = (self) |) というオブジェクトは self という名前のスロットを持っていませんが、メソッドの中ではメソッドオブジェクトに対するメッセージ self によって ( | x = ( self). | ) が返されます。これは暗黙の self が実はメソッドオブジェクトで、またスロット parent を持っているからです。

とは言え、実装的には上で書いたようにはなっていないような感じなんですけどね(じゃあ今まで書いたのはなんだったの!と思ったらごめんなさい)。この証拠として "self" は予約語扱いになっていてスロット名としては使えないようです(つまり (| self* = 1 |) のような表現は統語違反扱いになります)。しかし Reference Manual を読む限りではメソッドオブジェクトはこれまで書いたようになっていますし、そうしたように実装することは可能なはずです。しかしそうなっていないのは効率のためなのかも知れません。他にもメソッドスロットが再定義不可能だったり、メソッドオブジェクトまわりは理論と実装に多少の乖離があるような気がします。

さてところで、もちろんプロトタイプベース言語だから変数が無い必要があるというわけではありません。プロトタイプベース言語だけど Self っぽさのない Javascript には変数があります。あまり詳しくないけれども NewtonScript にも局所変数があるようです(少なくとも用語としては変数って言ってるのでそう判断しました。実際どうなのかは自信がないので各自確かめてみて下さい。もし間違っていたら教えて下さい)。

じゃあ Self の精神を色濃く受け継ぐ iolanguage はどうなの?

Io> x := 1
==> 1

これは一見すると局所変数のように見えますが、ちゃんとスロットに対する値の割り当てになっています。

Io has three assignment operators:

operator action

= Creates slot, creates setter, assigns value

:= Creates slot, assigns value
= Assigns value to slot if it exists, otherwise raises exception

These operators are compiled to normal messages whose methods can be overridden.

http://www.iolanguage.com/scm/git/checkout/Io/docs/IoGuide.html

実際やってみます。

Io> slotSummary
==>  Object_0x805e5c0:
  Lobby            = Object_0x805e5c0
  Protos           = Object_0x805e058
  exit             = method(...)
  forward          = method(...)

Io> x := 5
==> 5

Io> slotSummary
==>  Object_0x805e5c0:
  Lobby            = Object_0x805e5c0
  Protos           = Object_0x805e058
  exit             = method(...)
  forward          = method(...)
  x                = 5

Lobby オブジェクトにスロット x が作成されました(ちなみに iolanguage のスロットは全て read/write slot のようです)。以上のように iolanguage は Self 同様に局所変数を持ちません。でもこれらの演算子によってまるで局所変数があるのと変わらないくらい便利に使えます(Self ではこれらの代わりに _AddSlots: メッセージをいちいち送る必要があります)。さすが!

でも、Self にせよ iolanguage にせよ、スロットに局所変数相当のなんか変なのが追加されるの、嫌じゃないですか?基本的には局所変数のようなものが必要のないようにプログラムを組むべきではあるはずなのですが(実際のところ、Self world はなるべくそうした必要のないように設計されているようですから、局所変数がなくても困らないはずです)、どうしても欲しい場合もあるでしょう。Self ではそんな時にはブロックオブジェクトを使えば良いと思います(iolanguage だとメソッドオブジェクトでも大丈夫ですよね)。

例えば以下のようなつまならない例(rubyで書きました)を考えてみます。

x = rand(20)
p (if x < 10 then x else 10 end)

こうした状況は Self では次のように書けば良いものと思われます。

 [|:x| x < 10 ifTrue: x False: 10] value: random integer: 20

これを ruby で書き直すと次のような感じになります。

proc {|x| if x < 10 then x else 10 end}.call(rand(20))

このようにブロックオブジェクトを利用して頑張れば(実際的なプログラムを組む場合にはひどくネストしたブロックを作っちゃいそうです)、私のような凡人でも局所変数のない美しいプロトタイプの荒野を生き残れるのではないかと考えています。

で、実際これでいいの?という疑問がどうしても脳裏を駆け巡るわけですが、本当はどうするべきなんでしょうか。誰か教えて下さい。iolanguage なら実用している人が居ると思うのですが、局所変数がなくて困る事、ありませんか?

要素代入演算子式に関する落とし穴

Ruby の要素代入演算子(演算子でいいのかな?)は定義が可能です。

class A
  def x=(a)
    return 1
  end
end

上の定義は A.new.x = y の形で y の値にかかわらず常に 1 を返すように意図したつもりですが、実際には上手くいきません。

A.new.x = 5 #=> 5
A.new.x = 100 #=> 100

つまり、y の値が返されます。これは 1.8, 1.9 に共通した振舞いです。私はこれまで要素代入演算子式はただの糖衣構文だと思い込んでいたのですが、実は return された値を見ないという特徴があるようです。私と同様の思い込みをしている人は注意しておいて下さい。

このせいで Selfish は (_(:x => nil).x = 10).x のように書けないんですよね。一方 Self においてはスロットの割り当ては self を返すので ((|x <- nil|) x: 10) x のように書くことが出来ます。

Selfish: Ruby で Self 的プロトタイプベース!

Ruby に Self 的なプロトタイプベースオブジェクト環境を導入する Selfish というものを作りました。

http://github.com/keita/selfish/tree/master

これは、Self に興味があるけど試せない人のために、Ruby でそれっぽい感じに Self の雰囲気を再現しようという試みです。今はまだとりあえず試作段階のために、オブジェクトの作成やメソッドの呼び出し、継承など、基本的骨格しか出来ていません。最終目的は Self world を実現することなので、これから少しづつ Self の objects/core にあるオブジェクト定義を Selfish に移植していきたいと考えています。

どういう風に、どれくらい、それっぽいのか

あくまでも Ruby なので、あまり期待しないで下さい。でも可能な限りは、見た目も中身も、似せています。

オブジェクト

空のオブジェクトの作成するには次のようにします。

  • Self: ()
  • Selfish: _()

スロット"x" の値が 1 であるようなオブジェクトは次のように作成します。Selfish では常に read/write slot が作成されることに注意しておいて下さい(read only slot は現在のところ作成できません、どう記述するかのアイディアもありませんので対応しないかも知れません)。

  • Self: (|x <- 1|)
  • Selfish: _(:x => 1)

parent slot "parent" を持つオブジェクトは次のように作成します。Self ではスロット名の末尾に * を付けることで parent slot を指定しますが、Selfish では頭に _ を付けることで指定します。Self では頭に _ を付けた場合 primitive method を指しますが、Selfish ではこのように異なる役割を与えているので注意して下さい。

  • Self: (|parent* <- ()|)
  • Selfish: _(:_parent => _())
メソッド

引数なしのメソッドを定義、呼び出すには次のように書きます。Selfish ではメソッドの定義をブロックを使って行ないます。ブロックの第一引数に self が束縛されますので、これを利用してオブジェクトにメッセージを送って下さい。とても残念なことですが、Self と違って self を省略できません(いずれこれは改善出来るかも知れません)。

  • Self: (|x <- 1. call = (x)|) call
  • Selfish: _(:x => 1, :call => proc {|s| s.x}).call

引数のあるメソッドを定義、呼び出すためには次のように書きます。Self では引数を複数渡す場合にはキーワードを利用しますが、Selfish では単純にブロック引数として渡されます。

  • Self: (|next: = (|:x| x + 1)|) next: 1
  • Selfish: _(:next => proc {|s, x| x + 1}).next 1
Slot

データスロットの読み込みはスロット名をそのままメソッド名として呼び出して下さい。

  • Self: (|x <- 1|) x
  • Selfish: _(:x => 1).x

同様にデータスロットの書き込みは次のようになります。

  • Self: (|x <- 1|) x: 2
  • Selfish: _(:x => 1).x = 2

ただし、Self では上の表現の返り値は (|x = 2|) というオブジェクトになりますが、Selfish では書き込んだ値である 2 になります。これは Ruby の制限のようなので仕様ということになります。このため、例えば Self では ((|x <- 1|) x: 2) x のように書けますが、Selfish では (_(:x => 1).x = 2).x のようには書けません。

継承

継承を行なうためには、parent slot を作成すれば良いだけです。

  • Self: (|parent* <- (|sum = (x + y)|). x <- 1. y <- 1|) sum
  • Selfish: _(:_parent => _(:sum => proc {|s| s.x + s.y}), :x => 1, :y => 2).sum

多重継承は次のようになります。

  • Self: (| parent1* <- (|a = (1)|). parent2* <- (|b = (2)|) |)
  • Selfish: _( :_parent1 => _(:a => proc {1}), :_parent2 => _(:b => proc {2}))

ただし、Self と同様に複数スロットが見つかった場合には例外が発生しますので注意して下さい。

まとめ

結構それっぽくないでしょうか。継承は結構そのままな感じに振舞っているつもりですので、プロトタイプベースで遊びたい人は是非試してみて下さい。現在は gem にしていませんが、いずれ gem にして簡単にインストール出来るようにする予定です。

パッチ絶賛募集中ですので、なにか変なところを見つけたら、お教え下さいませ。よろしくお願い致します。

脆弱性って何かを知りませんでした!

以前セーフレベル4関係で送ったパッチが脆弱性ということで報告されててまぁびっくり。

Rubyに複数の脆弱性

なぜびっくりかと言うと、私はこれらを脆弱性だと認識していなかったからです(特に untrace_var のは)。「セキュリティーホール」とかの言葉とごっちゃになっていたのですが、こういうものも脆弱性と呼ぶのですね。

システムの脆弱性となるのはハードウェアの欠陥やソフトウェアのバグが多いが、こうした明白な欠陥だけが脆弱性になるわけではなく、開発者が予想しなかった利用形態や設計段階での見落としなど、形式的には欠陥とはならない潜在的な問題点が脆弱性として後から認知されることも多い。

http://e-words.jp/w/E88486E5BCB1E680A7.html

今回のは「開発者が予想しなかった利用形態」に相当するのでしょうか。これまで認識不足でごめんなさい。

さて、セーフレベル4に関する似たような問題点は、細かいものを含めればまだたくさんあった気がするというかいくつか報告してないものもあるので、現在のところセーフレベル4環境には頼らない方が良いと思います。本来の「信頼できないプログラムを実行する」という意味ではほとんど誰も使ってないと信じたいところです。プログラムのプラグイン(的な機構)がひょっとしたら信用できないかもなぁ、という時の気休めくらいに使っておくのがいいですよ。リファレンスマニュアルに書いてあるようにセーフレベル4環境の安全性は一切保証されていませんから、使うのならば覚悟してソースを読んで自分の身を守るべく行動するしかないのだと思います。

でもどうしても使わざる得ない時には、

  • 拡張ライブラリは Ruby 本体についてくるものであっても使わない。使う時はソースを読んで安全を確認してから。
  • 信頼出来ないプログラムを実行する前に全てのオブジェクトの汚染フラグを取り除いておくこと。セーフレベル0においても汚染されるオブジェクトが存在することを忘れてはなりません。

という事に気をつけておいて下さい。この二つを守るだけでぐっと安全になるだろうと思います。でもこれらを守っても防げない嫌がらせ的なものは、そう、今ひとつ思い出しました。どうしよう。嫌がらせって脆弱性の範疇なのでしょうか?

セーフレベル4は、現在のところ拡張ライブラリがちっともセーフレベルの存在を考慮していないという環境面での問題と、セキュリティーモデルにおいて連続的に存在しているレベル0や1との整合性があまりないという問題をかかえています。前者はみんなで頑張ってパッチを送りまくるだけで解決するので人海戦術的な話で楽勝だと思います。また後者は trust/untrust モデル(現在のレベル 0,1,2 における taint モデルと 3, 4 を切り分けるという話です)だかに変更される予定になっているはずですので、将来的には万事解決するはずです。やったね!本当は今の連続性のあるセキュリティーモデルの方がカッコイイと思うのですが、扱いづらいという点は確かですので、うまく改善されるといいなぁと願っています。

あれ、でも 1.8 に新しいセキュティーモデルが採用されるわけがないから、どのみちセーフレベル4の問題点は潰しておかないとダメなのでしょうか。そんな心配が要らなくなるように早く 1.9 の時代が来るといいですね。

というわけですので、皆様も是非 Ruby のセキュリティーモデルに興味を持って遊んでみて下さい。「$PROGRAM_NAMEがセーフレベル4で変更可能」だとしたら一体何が出来そう?とか考えると、Ruby の標準添付ライブラリのあり方にとっても詳しくなれると思います!

とっても優しい Self の書き方

実際に書かないと分からないので色々やってみました。統語規則は大体のところ Smalltalk っぽいので(多分統語だけじゃなくって world のあり方とかその他の大部分も相当似てるような感じです、なんというかプロトタイプベース版 Smalltalk みたいな?)、先日勉強した成果がこんなところに活かされました!やったね!

なおリファレンスマニュアルがとっても読み易いのでお得な感じです。以下からダウンロードできます。本記事はリファレンスマニュアルを参考にちまちまとコードを試しながら書きました。

http://research.sun.com/self/language.html

ところで、Self は SELF って大文字で書くのと Self と先頭大文字にするの、どっちが正しいのでしょうか。私はもう後に退けないから Self って書いていくことにします。

オブジェクトの作成

オブジェクトはスロットとコードから構成されます。コードを持たないオブジェクトを data object と呼びます。

"スロットをひとつも持たない空の data object"
()

"() と同じ"
(||)

"スロット a を持つ data object、ただしスロット a の中身は nil"
(|a|)

"スロット a の中身は 1、ただしスロット a を他に変えることは出来ません"
"スロット a は read-only slot となります"
(|a = 1|)

"スロット a の中身は 1、スロット a に対する割り当てが可能"
"スロット a は read/write slot となります"
(|a <- 1|)

"複数のスロットを設定する時には . で区切ること"
"この場合には a, b, c の三つのスロットを持つオブジェクトが作成されます"
(|a. b. c|)

空の data object である () はスロットに本当に何も持たないので、() = () のように比較すら出来なくてびっくりします(ただし _Eq: という primitive が存在するので () _Eq: () で一応比較出来なくはありません。また本来は default behavior というものであらかた振舞いが得られるようになっているようなのですが、() に関してはそれも利かないようです)。

スロットの内容を取り出したい時には、そのスロット名をメッセージとして送ります。

(|a = 'abc'|) a
"=> abc"

read/write slot は例えば (|a <- 1|) の時、a: という名前のスロットが作成されて、その内容が割り当てメソッドになります。Ruby でいうところの attr_accessor のことですよね。記法が簡単でとってもお得だと思います。

"a に 10 を割り当て"
(|a <- 1|) a: 10

メソッドの作成

では次にメソッドを作成してみましょう。メソッドはコードを持つオブジェクトで、parent と arguments という名前のスロットを持ちます。

"a = ('a' printLine) の部分がメソッドの作成、これもオブジェクト"
"最後の a で作成したメソッドを呼び出しています"
(| a = ('a' printLine) |) a
"=> a"

"ちなみに以下はなぜかダメ、良さそうなものなのに"
(| a = (|| 'a' printLine) |)
No '||' slot found in a slots object.

なお、メソッドはメソッドの中では作成できません。そういう目的にはブロックを使えということでしょうか。

(| a = (|a| (|b| 'a' printLine)) |)
inner methods are no longer supported, slot-list within a sub expression is not legal.

次に引数を持つ複数持つメソッド(keyword methods)を作成します。それぞれの引数には対応するキーワードを設定しますが、第一キーワードは最初の文字が小文字もしくは _ になります(ただし _ から始まる場合は primitive になるそうですので、通常は小文字で始めておけばいいと思います)。下の例では a: の部分が第一キーワードになります。第二キーワードは大文字から始めなければなりません。下の例では B: の部分が第二キーワードになります。引数は :a や :b のように前にコロンを付けてやるだけです。メソッドを呼ぶ時にはキーワード付きで呼び出します。

(| a: B: = (|:a. :b| a + b) |) a: 1 B: 2
"=> 3"

他にも unary methods やら binary methods やらありますが、特に難しいことや紛らわしいこともないのでリファレンスマニュアルをご覧下さい。

最後におまけのミニ情報ですが、メソッドの再割り当ては実装されてないそうです。エラーメッセージでごめんって言ってるってことは言語仕様的には本来認められるってことでしょうかね。

(| a <- ('a' printLine) |) a
"=> sorry - assignable methods not implemented."

ブロック

ではブロックを見てみましょう。ブロックはスロット value に不可視のブロックメソッドを持つオブジェクトだそうです。ブロックメソッドはあるけど見えないんだって。ブロックは次のようにして作ります。

[|:a| a * a]

これはメソッドオブジェクトとほとんど同様ですが、暗黙的レシーバ self がブロック作成時の self になる点で異なります。つまり早い話が Smalltalk とか Ruby のブロックと一緒の働きですよね。

ブロックを呼び出す時には次のようにします。

[1] value
"=> 1"

[|:a| a * a] value: 2
"=> 4"

[|:a. :b| a * b] value:2 With:3
"=> 6"

なんか With: をたくさん重ねちゃえば引数をいくつも渡せるみたいです(こういうメソッドの定義方法が分からないのですが、これって block value だけの特別扱い?)。With: も第二キーワード以降になるから、メソッド呼び出しの時と同様に大文字で始まっています。

delegation による継承

さて肝心の delegation による継承システムを見てみましょう。delegation とは child が parent の振舞いを借りて self を child として実行することです。

delegation って何? - ¬¬日常日記

Self においては parent となるオブジェクトをスロット(parent slot)で指しますが、この時末尾に * を付けます。

"parent* に割り当てられた traits pair が parent オブジェクト"
(|parent* = traits pair|)

あくまでも delegation であって consultation ではないので次のようものはうまく行きません。

(|parent* = 'abc'|) size
"=> badTypeError: the '_ByteSize' primitive failed."
"   Its receiver was a slots object."

ちなみに parent slot は必ずしも read-only slot である必要があるわけではありません。よって次のように parent slot の再割り当てを行なうことも可能です。

(|parent* <- traits pair|) parent: traits point

parent slot は末尾に * を付けることで作成します。従ってスロット名が parent である必要もなければ(ただし慣習的には parent* とするようです)、一つのオブジェクトに複数の parent slot を作成することも出来ます。このことから分かるように Self では実質的に多重継承が可能です。

"a* に割り当てられた trais pair と
"b* に割り当てられた traits point の両方が parent"
"ちなみに _AddSlots で shell にスロット obj を割り当ててます"
_AddSlots: (| obj = (|a* = traits pair. b* = traits point. x. y|) |)

obj x: 10
obj y: 2

"セレクタ aspectRatio は traits pair のスロットにマッチします"
obj aspectRatio
"=> 0.2"

"セレクタ asRectangle は traits point のスロットにマッチします"
obj asRectangle
"=> a rectangle(0@0 # 10@2)"

しかし多重継承をやっちゃうとセレクタが複数のスロットにマッチしてしまうという問題点があります。この時どうもエラーになるようです。どちらのスロットを優先するかといった制御が可能なのかどうかはまだ良く分かっていません。

"copyX:Y:は traits pair と traits point の両方に存在します"
obj copyX: 1 Y: 2
"=> More than one 'copyX:Y:' slot was found in a slots object."
"   The matching slots are: traits point and traits pair."

よって実際のところはこうした多重継承を用いるのではなく、Ruby 同様により安全な Mixin を使用するのが良いようです。とは言えば mixin もただのオブジェクトで、parent slot として複数設定しても困らないように parentless で安全に設計されているだけのようです。

Self で循環継承

じゃあいつもの通り循環継承にチャレンジしてみたいと思います。

"最初に parent が空のスロットを作成します"
_AddSlots: (| obj = (|parent*|) |)

"次に parent を自分自身に設定します"
obj parent: obj

"ではいざメソッド探索に出発!"
obj x
"=> No 'x' slot found in a slots object."

循環継承できちゃいました!しかも未知のメソッドを探索してもきちんと停止します。実は Self は循環継承を言語仕様において明示的に許可している珍しい言語なのでした。リファレンスマニュアルから該当個所を引用します。

2.3.8 The lookup algorithm
The lookup algorithm recursively traverses the inheritance graph, which can be an arbitrary graph (including cyclic graphs). No object is searched twice along any single path.

つまりメソッド探索において一度探索した parent は二度目以降は探索しないことで循環継承に対応しています。私はこれが一番正しい循環継承の扱い方だと思っています。

というわけだから、Javascript って循環継承の扱いだけでなく多重継承も Mixin もないわけで(実質的に同じことは出来るのだろうとは思いますので実際的な不満があるわけではないのですが)色々と骨抜きだよなぁ、と思いました。ひょっとして Javascript はプロトタイプベースとは言え Self の影響をあまり受けなかったのでしょうか?

__proto__ で循環継承 - ¬¬日常日記

まとめ

大体のところこんな感じです。とっても過激にオブジェクトしてて美しいですね!統語は簡潔だし、意味論も理解し易いので是非一度リファレンスマニュアルをご覧下さい。プロトタイプベースの言語と言えば javascript と思っていましたが、その礎たる Self はもっと優れた言語であるように思いました。まさかこんなに良く出来てるとは予想もしませんでした、すごいよ Self さん!

とは言え、これまで全然触れなかったことからも分かるように、ローカル変数とかそういうのが全然ないわけですよ。ローカル変数相当が欲しけりゃブロックでなんとしなさい!というのは分かるのですが、半端じゃなく面倒です。またオブジェクトにスロットを簡単に追加する糖衣構文もなければ inner method も禁止されているので色々面倒だと思います。面倒なのは我慢するとしても、objects/core 以下に存在するソースを読めば分かるように実際的なプログラムにおける可読性が極めて悪いのは簡単に予想できます。マクロが好きじゃない私でもこれはさすがにマクロ欲しくなりました。

美しさの反面、これじゃ流行らないわけだよ!と思いました。やっぱりこう、うまくいかないものですね。しかしそこで iolanguage ですよ、と話は続く、ような気がします。

あとこれだけなんでもかんでもオブジェクトでなんでもかんでもメッセージ送りまくってりゃ、そりゃ VM で頑張るしかないよな、とも思いました。目的のためには手段を選ばずで VM の高速化手法を開発していったという歴史のようですから、なんというか頭の良い人達の無茶苦茶な頑張りっぷりには頭が下がります。こっそり GC も世代別 GC だったりするらしいし、言語設計の美しさと実装の剛腕さの両立は大変だったんだろうなぁと苦労がしのばれます。

とにかく面白い言語なので皆さんも是非 Self やってみて下さい。環境によっては動かすのが大変かも知れませんけどね。

Self 処理系を色々試してみました - ¬¬日常日記

__proto__ で循環継承

Javascript 実装の一部には __proto__ というのがあるのですね。

http://developer.mozilla.org/en/docs/Differential_inheritance_in_JavaScript

これはまさに delegation における parent です。

で、なんでこれが非標準なのでしょうか。プロトタイプベース言語であれば少なくとも内部的にはどうせ __proto__ に相当するものを持たざる得ないわけですから、Javascript は最初敢えてこのスロットを隠したとしか考えられません。そうしたのには何か理由があるのだと思うですが、歴史的経緯とか分からないのでなぜそうしたのかが全く分かりません。利点がなにかあったのでしょうか。

ご存知の方、教えて下さいませ。よろしくお願い致します。

これだけだとつまらないからプロトタイプベース的に循環継承やっておきます!

var child = {}
child.__proto__ = child;

結果は次のようになりました。なお、インタプリタには SpiderMonkey を使用しています。

js: Cyclic __proto__ value not allowed.
js: org.mozilla.javascript.EvaluatorException: Cyclic __proto__ value not allowed.

というわけで、とっても親切な SpiderMonkey さんは、きちんと循環継承を禁止してくれました!

...などと納得して良いのでしょうか。というわけで Self はどうなっているのか調べていきたいと思います。

Self 処理系を色々試してみました

http://research.sun.com/self/

Self やるぞー!と鼻息をぷんぷんさせていたところ、純正 Self はとってもプラットフォームを選ぶことが判明しました。動くのは、

のみという狭さ。悔しい、MacOSX で動くのならば Linux で動いて欲しい。ていうか MacOSXPPCIntel も大丈夫って言ってるのに Solaris については SPARC 以外言及がないのがなんとなくもの悲しいところです。

なぜこんなにプラットフォームを限定しているの?と思うわけですが、これは Self の先進性であったらしい VMJIT で高速化を実現していたから、という事情のようです(つまり各プラットフォーム用に JIT 用意するのが大変とかそういうわけなのだろう、と想像しています)。え、JavaJIT の元が Self にあったりするわけなの? ついでに squeak の Morphic って Self 由来なの?

というわけで、Self はプロトタイプベースを実現した先進性だけでなく、実用性を目指して JIT を実現した、とても夢に溢れた言語であったわけですね。Cの半分くらいの速度で動いていたという話もどこかに書いてありましたから、なかなか大したものです。すごいですよね!

Self 処理系

さてしかし、これでは Self を試せません。そういうわけで純正をあきらめて他の処理系にあたってみました。

というわけで、 Self/x86 もしくは dSelf のみが希望を持てるわけですが、残念ながら dSelf も分散オブジェクトを実現するために Self と 100% 互換というわけにはいかないそうなので涙を飲んで我慢(動作確認まではしました、dSelf は dSelf でなんだか面白そうです)。よって私には Self/x86 しか選択肢はありません。

Self/x86 の使い方

Self/x86x86 であれば一応 linux でも cygwin でも動くそうです。ただしソースからコンパイルするのは難しそうです。というのもどうも古い gcc でしかコンパイル出来ないような感じだからです。なので素直にバイナリパッケージをダウンロードして使いましょう。

http://gliebe.de/self/download.html

tar.gz を展開すると Self という実行ファイルが出来ますので、これを実行すると

Self Virtual Machine Version 4.1.10, Sun 08 Dec 02 16:51:16 Linux
Copyright 1989-2002: The Self Group (type _Credits for credits)

VM#

のように表示されます。ようやく Self が試せますね!やったね!

でも Version 4.1.10 って、大部古いようですけど、大丈夫なのかな。とりあえず Empty.snap をダウンロードしてこれを使ってみます(smalltalk と同じで環境丸ごと保存して使いまわす感じなんですね)。

% ./Self -s Empty.snap

	Welcome to the Self system!  (Version 4.1.5)


Copyright 1992-2002 Sun Microsystems, Inc. and Stanford University.
See the LICENSE file for license information.

Type _Credits for full credits.

VM version: 4.1.10

"Self 1" X I/O Error .. aborting process.

おお、いきなりのエラー。メッセージの示すところはXに接続できないとかそういう事なのでしょうか。これでGUIは絶望的になりましたが、気にしない事にします。

"Self 1" lobby.
lobby

うん、とにかく動きました。不安が募るばかりですが、頑張ります。

まとめ

MacOSXの人はこんなに悩まなくていいから羨しいですね。LinuxWindows の人は Self/x86 で頑張るしかないので、不安を押しのけて頑張りましょう。こういう環境面での制約がなかったら、もっと Self って流行っていたり再流行したりしなかったのかな、と思います。