Self
Self は、「プロトタイプ」の概念に基づいたオブジェクト指向プログラミング言語である。1980年代から1990年代にかけて言語設計の実験的システムとして使われていたが、2018年、Self の開発は続けられており、Self言語自身で書かれた Selfバーチャルマシンを構築する Klein プロジェクトが進められ、2006年7月にバージョン 4.3 がリリースされた。
![]() Selfのロゴ | |
パラダイム |
マルチパラダイム: オブジェクト指向, プロトタイプ指向 |
---|---|
登場時期 | 1986年 |
設計者 | David Ungar、Randall Smith |
開発者 |
David Ungar、Randall Smith、 スタンフォード大学、 サン・マイクロシステムズ |
最新リリース | 2017.1/ 2017年5月24日 |
型付け | 動的、強い型付け |
主な処理系 | Self |
影響を受けた言語 | Smalltalk |
影響を与えた言語 |
NewtonScript、JavaScript、 Io、Cel、Agora |
ウェブサイト | www.selflanguage.org |
歴史
編集プロトタイプベース・プログラミング
編集Vehicle
クラスのオブジェクトが﹁名前﹂を持ち、﹁運転﹂とか﹁建材を運ぶ﹂といった機能を持つとする。Porsche
911
が Vehicle
クラスの1つのオブジェクト︵インスタンス︶で、﹁ポルシェ 911﹂という﹁名前﹂を持つとする。理論的には Porsche 911
に対して﹁建材を運べ﹂というメッセージを送ることができる。
この例はオブジェクト指向のはらんでいる問題を示している。ポルシェはどう考えても建材を運ぶのには適さないが、モデル化された Veh
icle
にはその機能が与えられている。より適したモデルを生成するには Vehicle
に特殊化を施したサブクラスを使えばよい。例えば、Sports Car
とか Flatbed Truck
である。この場合、Flatbed Truck
クラスのオブジェクトだけが﹁建材を運ぶ﹂という機能を持てばよい。一方、Sports
Car
にその機能を与えるのは間違いであり、単に高速に運転できればよい。
このような問題がプロトタイプという考え方を生む要因となった。クラスやオブジェクトが将来どのような機能を持つようになるか確実に予測できないと、クラス階層を正しく設計することはできないのである。Smalltalkのような初期のオブジェクト指向言語では、この手の問題が頻繁に生じた。システムがある程度まで成長すると、全体として硬直化が起きて、特に基本的なクラス群は相互の関連に縛られ、機能追加が困難となる。
Smalltalk のような動的言語では、クラスの変更によって容易にオブジェクトの振る舞いを変えられるという特徴でこれに対処できる。一方で、この変更は影響を及ぼす範囲を考えて注意深く行うべきものである。変更したクラスに属するオブジェクトが、﹁意図していない、挙動が変更されるべきでない、誤った、﹂動きをしないように。この問題は﹁脆弱な基底クラス問題﹂の一種である。
C++のような、基底クラスと派生クラスを別々にコンパイルできる言語では、事前にコンパイルした派生クラスのメソッドに、基底クラスでの変更が影響しないが、これはまた別の問題を生じる。これは前述の﹁脆弱な基底クラス問題﹂の別の形であり、また、脆弱なバイナリ・インターフェース問題の一種でもある。
Self や他のプロトタイプベース言語では、クラスとオブジェクトの双対関係が排除されている。
何らかの﹁クラス﹂に基づくオブジェクトの﹁インスタンス﹂を作るのではなく、Self では既存のオブジェクトをコピーし、それに修正を加える。従って、Porsche 911
を作るには、他の Vehicle オブジェクトをコピーし、﹁高速運転﹂メソッドを追加すればよい。コピー元となるオブジェクトを﹁プロトタイプ﹂と呼ぶ。この技法によりダイナミズムが劇的に単純化されると言われている。既存のオブジェクトがモデルとして不適切であった場合、プログラマは単にオブジェクトに修正を加えて正しい振る舞いをするようにして、それを新たなプロトタイプとして使えばよい。既存のオブジェクトを使っているコードはそのまま使うことができる。
言語としての記述
編集Self のオブジェクトは「スロット」の集まりである。スロットとは、値を返すメッセージであり、スロット名の後にコロンをつければ値をセットするメッセージになる。例えば、"name" というスロットがあるとする。
myPerson name
これは name の値を返す。
myPerson name:'gizifa'
これは値をセットする。
Self は Smalltalk と同様「ブロック」を使って処理の流れを制御する。メッセージを受け取るメソッドはスロット群以外にコードを持つオブジェクトであり、任意のスロットの値としてメソッドを格納できる。メソッドの持つスロットは引数や一時変数として使われる。いずれの場合も文法的には同じである。
Self ではフィールドとメソッドに区別はなく、どちらもスロットである。メッセージによるスロットアクセスで Self の文法の大部分が説明されるため、自分自身(self)へのメッセージも多い。そのため "self" は省略できる(また、これが言語名の由来)。
基本文法
編集receiver slot_name
二項
receiver + argument
キーワード
receiver keyword: arg1 With: ar
g2
どのメッセージも値を返すので、receiver や argument もメッセージ形式をとることが可能である。メッセージの後ろにピリオドをつけると、リターン値を捨てることを意味する。例えば、
'Hello, World!' print.これは Self によるHello worldプログラムである。
'
︵シングルクォート︶はリテラル文字列オブジェクトを意味する。その他のリテラル︵即値︶として、数、ブロック、汎用オブジェクトがある。
グループ化は括弧を使って明示される。明示的なグループ化をしない場合、優先順位は単項メッセージが最も高く、次に二項メッセージ︵左から右にグループ化︶、キーワードメッセージは最も優先順位が低い。代入にキーワードを使うとき、式にもキーワードが含まれている場合に追加の括弧が必要となる。それによって最初のキーワードメッセージセレクタを小文字から開始したり、次の部分を大文字で開始したりといった必要がなくなる。
valid: base bottom between: ligature bottom + height And: base top / scale factor.この一文は曖昧さがなく、次のものと同じに解釈される:
valid: ((base bottom) between: ((ligature bottom) + height) And: ((base top) / (scale factor))).Smalltalk-80 では、同じ式が次のように記述される:
valid := self base bottom between: self ligature bottom + self height and: self base top / self scale factor.
新しいオブジェクトの生成
編集もう少し複雑な例として次の記述を示す:
labelWidget copy label: 'Hello, World!'.
"labelWidget" オブジェクトへの copy メッセージでコピーを作り、そのコピーの "label" スロットに "Hello, World" メッセージを格納すべくメッセージを送っている。これを使ってみると次のようになる。
(desktop activeWindow) draw: (labelWidget copy label: 'Hello, World!').
この場合、(desktop activeWindow)
が最初に評価され、desktop オブジェクトが知っているウィンドウのリストからアクティブウィンドウを表すオブジェクトが返される。次に(内側から外側へ、左から右へという順で)前掲のコードが評価され labelWidget が返される。最後にそのウィジェットがアクティブウィンドウの draw スロットに送られる。
継承/委譲
編集特徴
編集例えば、「銀行口座」というオブジェクトを定義し、口座の残高を管理するとしよう。一般にこのようなオブジェクトには「入金」と「出金」のメソッドが作られ、それに必要なデータスロットも作られる。これはプロトタイプであるが、そのまま汎用の口座を表すオブジェクトとしても使える。
このオブジェクトのクローン「ボブの口座」を作ることで、上記オブジェクトがプロトタイプとして使われたことになる。このとき、そのオブジェクトが持つ全てのメソッドやデータがスロットとしてコピーされる。しかし、より典型的な手法は、もっと単純な traits object(特徴オブジェクト)と呼ばれるオブジェクトをつくり、そこにクラスに関連するものを含める。
この例では、「銀行口座」オブジェクトに入金や出金メソッドを備えさせるのではなく、その親オブジェクトに持たせる。このようにすると、多数の銀行口座オブジェクトのコピーを作っても、親オブジェクトの修正によって全銀行口座オブジェクトの動作を更新できる。
これはいわゆるクラスと何が違うのだろうか? 次の意味を考えてみよう:
myObject parent: someOtherObject.
これは、'parent*' スロットに適当なオブジェクトを代入することで myObject の「クラス」を実行時に動的に変更できることを意味する(アスタリスクはスロット名の一部だが、メッセージには表記されない)。継承やスコープとは違い、委譲オブジェクトは実行時に変更可能である。
スロット追加
編集_AddSlots: (| vehicle <- (|parent* = traits clonable|) |).'_AddSlots' プリミティブの受信者オブジェクトが明示されていないので、これは "self" に対するものである。これを対話型のプロンプトで入力すると、"self" オブジェクトに相当するのは "lobby" と呼ばれるオブジェクトである。'_AddSlots' の引数はオブジェクトであり、そのスロットが受信者オブジェクトにコピーされる。この例では、それは1つのスロットだけを持つリテラルオブジェクトである。スロット名は 'vehicle' で、その値が別のリテラルオブジェクトとなっている。"<-" という記号は、第一のスロットの値を変更するのに使われる 'vehicle:' という第二のスロットを暗に示している。 "=" は定数スロットを意味するので、'parent*' には対応する 'parent:' が無い。このリテラルオブジェクトは 'vehicle' の初期値であり、クローン作成に関するメッセージを理解できるスロットを1つもっている。完全に空のオブジェクトは、(| |) あるいは単に () で示され、メッセージを全く受け付けられない。
vehicle _AddSlots: (| name <- 'automobile'|).これも、前と同じオブジェクトが受信者であり、'parent*' に加えて新たに 'name' と 'name:' スロットが追加されている。
_AddSlots: (| sportsCar <- vehicle copy |). sportsCar _AddSlots: (| driveToWork = (何かのコード、これがメソッドになる) |).以前の 'vehicle' と 'sportsCar' はほとんど同じだが、ここでは後者にオリジナルが持っていなかったメソッドを伴うスロットが含まれている。メソッドを持つことができるスロットは定数スロットだけである。
_AddSlots: (| porsche911 <- sportsCar copy |). porsche911 name:'Bobs Porsche'.新たなオブジェクト 'porsche911' は 'sportsCar' とほぼ同じだが、最後のメッセージでその 'name' スロットの値が変更されている。これらはスロットの値は異なるものの、持っているスロットは同じであることに注意されたい。
環境
編集性能
編集ガベージコレクション
編集Self のガベージコレクションは世代型であり、オブジェクトを世代で管理する。メモリ管理システムを使ってページへの書き込みを記録し、ライトバリアを保つ。この手法は性能がよいが、ある期間実行を行っていると全体ガベージコレクションが発生し、無視できない時間を取られてしまう。
最適化
編集関連項目
編集外部リンク
編集- Self | Welcome 公式ウェブサイト
- Self Home Page at Sun Microsystems
- Papers on Self from UCSB (mirror for the Sun papers page)
- Self resources at Cetus Links
- Merlin Project
- Self ported to Linux (without many optimizations)
- Automated Refactoring application on sourceforge.net, written for and in Self
- Gordon's Page on Self
- Prometheus object system on the Community Scheme Wiki
- Video demonstrating self