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 にして簡単にインストール出来るようにする予定です。

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