コンテンツにスキップ

英文维基 | 中文维基 | 日文维基 | 草榴社区

「プロトタイプベース」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
G000001 (会話 | 投稿記録)
m 少なくともFlavors、CLOS、CLOS MOPの歴史とプロトタイプベースの歴史は直接的にリンクしていませんので、Flavors、CLOS、CLOS MOPがプロトタイプベースであるような記述は削除しました。(ANSI規格に歴史の記述があります: http://www.lispworks.com/documentation/HyperSpec/Body/01_ab.htm )
m 誤字修正。
 
(4人の利用者による、間の11版が非表示)
1行目: 1行目:
{{出典の明記|date=2015年12月}}
{{出典の明記|date=2015年12月}}
[[ファイル:Smalltalk metaclass.png|境界|右|フレームなし|265x265ピクセル]]
{{プログラミング・パラダイム}}


'''プロトタイプベース''' ({{lang-en-short|''prototype-based''}}) は、[[オブジェクト指向プログラミング]]のサブパラダであり、{{仮リ|メタオブジェクトプロト|en|Metaobject|label=}}中心した手法す。インスタンスベース(''instance-based'')とも呼ばれる。プロトタイプベースは1990年代前半から認知されるようになった後発のスタイルであり、先発の[[クラスベース]]のオブジェクト指向とは明確に区別されている。「[[Self]]」「[[Oz (プログラミング言語)|Oz]]」「[[Lua]]」「[[Python]]」「[[JavaScript]]」「[[Ruby]]」「[[Etoys]]」「[[ECMAScript]]」「[[REBOL]]」「[[Io (プログラミング言語)|Io]]」「[[TypeScript]]」といった[[スクリプト言語]]や[[Webプログラミング|Webプログラミング言語]]を中心に支持されている。
'''プロトタイプベース''' ({{lang-en-short|Prototype-based}}) は、[[オブジェクト指向プログラミング]](OOP)スタルのひとつであり、[[オブジェクト (プログラミグ)|オブジェクト]]の生成に既存[[オブジェクト (プログラミング)|オブジェク]]の複製を用いるスタイルを指している。これは直後にメンバ拡充るための空オブジェクトの複製も含まれているこのスタイルは、インスタンスベース(Instance-based)とも呼ばれている。これと対比されるOOPスタイル[[クラスベース]]がある。


プロトタイプベースOOPの原点は[[Smalltalk]]方言の[[Self]]であり、Smalltalkのクラスベース設計を平易化する試みから1987年に誕生している。他には[[Lua]]、[[JavaScript]]、[[Etoys]]、[[ECMAScript]]、[[REBOL]]、[[Io (プログラミング言語)|Io]]、[[TypeScript]]などがある。
プロトタイプベースではプロトタイプ、[[インスタンス]]、シンボル、[[プロパティ (プログラミング)|プロパティ]]、[[メソッド (計算機科学)|メソッド]]などは全てオブジェクトとして扱われる。オブジェクトはメンバオブジェクトの[[識別子|識別名]]&[[参照 (情報工学)|参照値]]をペア定義できる可変サイズの[[構造体|データ構造体]]であり、この形態はフレーム&スロット(''frame&slot'')とも呼ばれる。プロトタイプを複製するという方式で[[インスタンス]]が生成される。プロトタイプは動的にカスタマイズできる「[[型システム|型]]」と見なせるものであり、その型を複製したインスタンスは自由に加工/計算/代入できる「[[値 (情報工学)|値]]」になる。なお、静的な事前定義の機能が強調されたプロトタイプを「クラス」の名称で実装しているプロトタイプベースOOP言語も多いが、クラスベースのそれと区別するために本稿ではプロトタイプで統一する。


プロトタイプとは、複製元になった[[オブジェクト (プログラミング)|オブジェクト]]を意味しており、複製先のオブジェクトから見てそう呼ばれる。プロトタイプは同時にそのオブジェクトの暗黙の[[委譲]]先になり、これはプロトタイプを複製が[[継承 (プログラミング)|継承]]していることと同じになる。
== 特徴 ==
{{Quotation|''You make prototype objects, and then … make new instances. Objects are mutable in JavaScript, so we can augment the new instances, giving them new fields and methods. These can then act as prototypes for even newer objects. We don't need classes to make lots of similar objects… Objects inherit from objects. What could be more object oriented than that?''<br/>(あなたはプロトタイプを作りそして新しいインスタンスを作る。オブジェクトは可変であり新しいフィールドとメソッドを付け足して拡充できる。丁度プロトタイプで為されるように。類似オブジェクトを作るためのクラスは必要ない‥オブジェクトも継承できる。これ以上のオブジェクト指向があるだろうか?)|Douglas Crockford(JavaScript developer)}}プロトタイプベースの土台である{{仮リンク|メタオブジェクトプロトコル|en|Metaobject|label=}}の採用方法は言語ごとに異なっているためにその実装方式も様々であるが、本稿ではなるべく共通仕様に沿うように説明する。


== 来歴 ==
=== オブジェクトとは ===
プロトタイプベースは、[[Smalltalk]]の[[クラスベース]]設計を平易化する試みから考案されたスタイルなので、Smalltalkの設計を知らないとそれが作られた理由も分からないものになる。ここでは[[アラン・ケイ]]が概略したSmalltalk設計の六項目を紹介して、クラスベースに関連する部分を和訳しておく<ref name="EarlyHistoryOfSmalltalk">{{Cite web|url=http://worrydream.com/EarlyHistoryOfSmalltalk/|title=The Early History Of Smalltalk|accessdate=2019-01|publisher=}}</ref>。{{Quotation|1, EverythingIsAnObject.(全てはオブジェクトである)
プロトタイプベースでのオブジェクトは、大まかに言うと「フレーム&スロット」「シンボル」「データ」「コード」の四要素から構成される。この四要素もまたオブジェクトである。データは変数値/定数値を指す。コードは関数式(引数有り無し/返り値有り無し)を指す。シンボルは識別子となり、データが束縛されたら変数名、コードが束縛されたら関数名になる。フレーム&スロットはシンボル(変数名/関数名)が集約される媒体であり「プロトタイプ」「インスタンス」になる。フレーム&スロットに集約された変数/関数はメンバと呼ばれる。プロトタイプは要self変数/関数にアクセスできないオブジェクトであり、インスタンス(=self)はそれができるオブジェクトである。プロトタイプは[[継承 (プログラミング)|継承]]して派生プロトタイプを定義できる。プロトタイプを複製(copy/close)する方式でインスタンスが生成される。プロトタイプとインスタンスは共にメンバ変数とメンバ関数を自由に付け足すことができる。シンボルは上述の四要素分類、プロトタイプ/インスタンス区別、オブジェクト型識別、リテラル(数値/論理値/文字列/列挙型)識別のための[[アノテーション|型アノテーション]]としても用いられており、後述のプロトタイプベースにおけるメタクラスはその用法でのシンボルである。シンボルはセレクタ(振る舞い指示子)としても使われる。シンボルにもメンバを付けることができる。


2, Objects communicate by sending and receiving messages (in terms of objects).
上述のメカニズムから、変数はdefaultスロットにデータ束縛シンボルが入ったフレーム、関数はdefaultスロットにコード束縛シンボルが入ったフレームと見なせるので、変数に自身のメンバ変数/関数を持たせることも可能であり、関数ではそのローカル変数/関数が自身のメンバ変数/関数として解釈される。defaultスロットは、フレームで暗黙的に最優先参照されるスロットである。フレーム&スロット、定数、変数、関数式、引数、返り値は、それぞれがお互いをメンバにできるので入れ子の連結関係で所有ないし所属し合えるようになっている。変数にメンバ関数/変数を付けると[[クラスベース]][[オブジェクト指向プログラミング|OOP]]の[[インスタンス]]と同等になる。関数も同様であり、関数の要selfメンバ変数/関数はそのローカル変数/関数と同義になる。メンバ変数付き関数とメンバ関数付き変数は、[[関数オブジェクト]]または[[クロージャ]]と同等なものになる。


3, Objects have their own memory (in terms of objects).(オブジェクトは自身の記憶を持つ)
=== デリゲーションとメッセージパッシング ===
オブジェクトのメンバ関係の連鎖メカニズムは、デリゲーション([[委譲]])によるオブジェクトのコミュニケーションを活性化させる。委譲の多用にはセレクタ(振る舞い指示子)を用いる言語の方が適している。セレクタは<code>object selector:</code>のように書式されてこれは<code>object</code>に<code>selector</code>を送るという風に解釈され、これはメッセージパッシングと呼ばれる。関数呼び出しとメッセージパッシングの違いは、前者は関数名がコンパイル時定義されているの対して、後者では関数名も実行時に解釈される文字列引数(セレクタ)として渡される点である。これを受けとるオブジェクトの共通窓口はメッセージレシーバーと呼ばれる。セレクタの実行時解釈によるオーバーヘッドが懸念されて、よく用いられるセレクタのレシーブ時には対応プロセスに自動ジャンプする機構が重視されるようになり、このセレクタによる自動分岐がメソッド機構のルーツになった。セレクタ=メソッド名であるのでメソッドコールは関数呼び出しと回帰的同義になり、やがて<code>object.method()</code>のように書式されるようになった。


4, Every object is an instance of a class (which must be an object).(全てのオブジェクトはクラスのインスタンスであり、クラスもまたオブジェクトである)
メソッドは、関数型言語で用いられる関数適用<code>func object</code>の書式を反対にしたものと解釈できる。<code>func</code>は関数式であり<code>object</code>は値である。値(=オブジェクト)を先にしてそれを軸にするメソッド様式では、値にそれ専用の式内容も含ませており、セレクタはそれを引き出すための式名になっている。式内容を引き出された値はそのまま演算に移るか、後続の引数を送られての演算に移る。演算後は状態変化した自分自身か、導出された他の結果値となりそれにまたセレクタが送られる。セレクタに応じる式内容を値が持ってない場合のミッシング発生時は、値が保有しているメンバ値たちにそのセレクタを送るという委譲が積極的に行われてミッシング発生は極力回避される。セレクタの効用はこの委譲を容易にするための書式という意味が強い。ミッシングエラー回避だけでなく、セレクタを受け取った時点で委譲先オブジェクトに変えたり、後続引数で対応できなかったらそこで委譲するという処理の多様性も表現できる。


5, The class holds the shared behavior for its instances (in the form of objects in a program list).(クラスはそのインスタンスたちが共有する振る舞いを保持する)
セレクタを用いない言語では、<code>object.method()</code>のようにメソッドコールする一般的なプログラムになる。オブジェクトのメソッドが引数付きでコールされてそこで指定処理に対応できない場合は、そのメンバオブジェクトに引数を渡してそちらに委譲する。その委譲先でも不可だった場合はそのまたメンバオブジェクトにたらい回しできる。オブジェクトはそのたらい回しの結果返ってきた値を自身の返り値にできる。


6, To eval a program list, control is passed to the first object and the remainder is treated as its message.}}(4)は[[数学的帰納法|数学的帰納]]な文章なので、[[インスタンス]]のひな型である[[クラス (コンピュータ)|クラス]]もまた[[メタクラス]]のインスタンス化であり、そのメタクラスもまた他のメタクラスのインスタンス化であると解釈される。これは元祖[[クラスベース]]の大きな特徴であったが、実際はもう少し複雑でメタクラスとクラスの間に中間媒体が挟まれていた。i=インスタンス/C=クラス/M=メタクラスとするとインスタンス化の流れは、M → C class → C → iとなり、C classが中間媒体である。中間媒体は不変の[[静的型付け]]用としてオリジナルを保存し、クラスは可変の[[動的型付け]]用であった。
=== ダックタイピングと構造的型付け ===
プロトタイプベースは動的な[[関数型プログラミング]]由来のパラダイムである。故にプロトタイプベースでは、メンバ(変数/関数)はオブジェクトに所属しているのと同時に、そのオブジェクトに結び付けられた[[写像]]のミニモジュールであると考えたほうが仕組み的には分かりやすくなる。メンバ変数はプロセス無しの写像である。メンバ変数アクセスは<code>instance.property</code>と書式されるが、これは仕組み的には<code>property instance</code>となっていて写像シンボル<code>property</code>をオブジェクト<code>instance</code>に適用してメンバ変数値を導出している。メンバ関数はプロセス付きの写像である。メンバ関数アクセスは<code>instance.method()</code>と書式されるが、これは仕組み的には<code>method instance</code>となっていて写像シンボル<code>method</code>をオブジェクト<code>instance</code>に適用してメンバ関数をコールしている。引数がある場合は<code>instance.method(a,b)</code>と書式されて仕組み的には<code>method instance a b</code>となっている。セレクタを用いる言語では、オブジェクトに写像シンボルのメッセージを送る書式になり<code>instance property:</code>や<code>instance method: #(a,b)</code>のようになる。写像シンボルをオブジェクトに適用できるどうかの判別は、適用時のオブジェクトスロットに写像シンボルが入ってるかどうかで実行時に決められる。<code>ガー</code>が<code>instance</code>に適用できればそれはアヒルオブジェクトという意味から、これが[[ダックタイピング]]と呼ばれる。


また、(3)で記憶(データ)はクラスとインスタンスの双方が持つが、(5)で振る舞い(メソッド)はクラスのみが保持するとされており、これも混乱を招きやすいネックになっていた。元祖クラスベース設計は理論面では美しかったが、実践面では甚だ複雑として開発陣からも不評になり、それは哲学的なインスタンス化チェーンと、クラスとインスタンスに対する振る舞いの妙に起因していた。
上述の[[写像]](メンバ変数/関数)を単体で識別する[[ダックタイピング]]に対して、写像のまとまりをセットで識別するのは構造的型付けと呼ばれる。一つの目的を表現するための写像のミニモジュールは[[トレイト]]などと呼ばれる。[[トレイト]]をオブジェクトに多重実装させるのは[[ミックスイン]]と呼ばれる。ミックスインは一つの目的に沿ったメンバ変数/関数のミニモジュールをワンタッチでオブジェクトに実装して、そのミニモジュールの特性(''trait'')でオブジェクトを分類できるようにする。オブジェクトのトレイト実装判別は、そのトレイトの変数/関数をオブジェクトも全て保有しているかどうかが基準にされてトレイト名自体は顧みられない。[[インタフェース (抽象型)|インターフェース]]と[[ミックスイン]]は共に実装継承の対象であるが、前者は抽象性重視の性質から[[クラスベース]]で扱われる事が多く、後者は構造性重視の性質からプロトタイプベースや[[関数型言語|関数型スタイル]]OOPで扱われることが多い。


その改善策としての、オブジェクトから[[クラス (コンピュータ)|クラス]]と[[インスタンス]]の概念を無くしてしまおうという案が、プロトタイプベースの原点になっている。この案は言語視点では、クラスを無くしてインスタンスに一元化するとも解釈できるので、インスタンスベース(Instance-based)と別称された。クラスのインスタンス化を、インスタンスのクローンに置き換えることでSmalltalkクラス設計の改善が図られ、[[Self]]はこの案に沿って制作されたSmalltalk方言であった。ここでは(1)の遵守が第一にされていた。
=== メタクラスとプロトタイプ ===
[[クラスベース]]とプロトタイプベースでは、[[メタクラス]]と名指しする対象が異なるので注意が必要である。クラスベースのクラスはシステムが保持する特殊なデータなので直接の閲覧/操作はできず、システムが提供するインターフェースを通してのみクラス構成情報の閲覧/操作ができるので、そのインターフェース機構がメタクラスとされる。それに対してプロトタイプはプログラマが自由に扱えるデータ構造体であり、直接その構成情報を閲覧/操作できるのでプロトタイプ自体がメタクラス機能を備えているが、プロトタイプベースではプロトタイプ=メタクラスとはしておらず、プロトタイプのtypeスロット(これはprotoスロットやclassスロットとも称される)に入るオブジェクトをメタクラスとしている。プロトタイプベースにおけるメタクラスとは、アドホック多相の[[アノテーション|型アノテーション]]の働きをするシンボルになっている。他言語の属性付き[[アノテーション]]と同様にシンボルにもメンバを付け足して多様な型情報を表わせる。オブジェクトのtypeスロットは原型元オブジェクトを指し、parentスロットは継承元オブジェクトを指している。


なお、[[C++]]/[[Python]]からの静的/動的クラスベース設計では、[[クラス (コンピュータ)|クラス]]と[[インスタンス]]を、型と値の役割に固定してインスタンス化の相互再帰を無くしたことから、インスタンスのみがオブジェクトになっている。[[メタクラス]]はクラス構成操作の[[リフレクション (情報工学)|リフレクション]]機能になっている。これは(1)の事実上の撤廃であった。
よく使われる定型的なプロトタイプはそのメンバ変数/関数を、[[クラスベース|クラスベースOOP]]の[[クラス (コンピュータ)|クラス]]と同様にあらかじめ静的定義するかされているのが普通である。この静的な事前定義の側面が強調されたプロトタイプを「クラス」と呼んでいるプロトタイプベースOOP言語も多いが、[[クラスベース|クラスベースOOP]]のそれとは性質的に異なる。プロトタイプは継承可能であり派生プロトタイプを定義できる。派生プロトタイプのparentスロットに入るオブジェクトが基底プロトタイプになる。多重継承可能な言語では複数の基底プロトタイプをparentスロットに入れることができる。


現在では、C++/Python発のクラスベース設計が多くの言語で採用されており、[[Self]]発のプロトタイプベースは少数派になっている。のみならずプロトタイプベースの代表格[[JavaScript]]、[[ECMAScript]]ではクラスベース構文が導入されるに到っており、[[TypeScript]]はクラスベースとプロトタイプベースの折衷にされている。プロトタイプベースはやや汎用性に欠けてオブジェクト指向の主流にはなり得なかったという結論になる。
プロトタイプを複製(clone/copy)する方式でインスタンスが生成される。この複製では、複製元プロトタイプとそのparentスロットからの基底連鎖チェーン上の全プロトタイプのメンバを積み重ねて、同名メンバ重複を一定の手順で解決したひとかたまりがインスタンスになる。同名メンバ重複の解決はもっぱら派生側から最初にサーチされたものが最優先される方法で行われ、多重継承時の関数ではC3線形化などのメソッド解決順序(''MRO'')が用いられる。プロトタイプベースのこの一つのインスタンスにまとめる生成方式は連結(''concatenation'')と呼ばれる。クラスベースでは基底クラスごとのインスタンスを数珠繋ぎしたものを一つのインスタンスにするという連鎖(''linkage'')の生成方式を採用しているのでここが決定的に異なっている。クラスベースのインスタンスはsuper参照を持つが、プロトタイプベースのインスタンスのparentスロットは空欄になる。typeスロットには複製元プロトタイプが入れられる。上述のメカニズムから、typeスロットにプロトタイプが入ってるフレームがインスタンス、typeスロットにシンボルが入ってるフレームがプロトタイプとも判別できる。


== 概要 ==
=== 原型関係と継承関係 ===
[[Self]]とは少し別の切り口から、[[Smalltalk]][[クラスベース]]設計の平易化を図った{{仮リンク|ダグラス・クロックフォード|en|Douglas Crockford}}は、[[JavaScript]]のプロトタイプベースをこのように概略している<ref>{{cite web|last=Crockford|first=Douglas|title=Prototypal Inheritance in JavaScript|url=http://crockford.com/javascript/prototypal.html|access-date=22 June 2021}}</ref>。{{Quotation|You make prototype objects, and then … make new instances. Objects are mutable in JavaScript, so we can augment the new instances, giving them new fields and methods. These can then act as prototypes for even newer objects. We don't need classes to make lots of similar objects… Objects inherit from objects. What could be more object oriented than that?<br/>(あなたはプロトタイプ・オブジェクトと新しいインスタンスを作る。オブジェクトは可変であり、新フィールドと新メソッドを加えて拡充できる。これもまた新生オブジェクトのプロトタイプになる。クラスは必要ない。オブジェクトも同様に継承できるからだ。これ以上のオブジェクト指向があるだろうか?)}}プロトタイプベースはプログラマに、オブジェクトをどう振る舞わせるかということのみに集中させて、オブジェクトが実際に振る舞えるかどうかの疑問を後回しにできる環境を提供する<ref>{{Cite journal|last=Taivalsaari|first=Antero|last2=Noble|first2=James|date=1998|title=Thinking with prototypes|url=https://doi.org/10.1145/346852.346949|journal=Addendum to the 1998 proceedings of the conference on Object-oriented programming, systems, languages, and applications (Addendum) - OOPSLA '98 Addendum|publisher=ACM Press|location=New York, New York, USA|doi=10.1145/346852.346949}}</ref>。振る舞いとは[[メソッド (計算機科学)|メソッド]]である。疑問の後回しとは[[動的型付け]]の[[ダックタイピング]]を意味している。プロトタイプベースは[[静的型付け]]の実装を排除してる訳ではないが、その特性と利点を最大限に活かすための[[動的型付け]]が好まれている。
クラスベースと異なりプロトタイプベースでは原型関係(typeスロット)の存在感が高く、継承関係(parentスロット)の存在感は低いものになっている。継承関係は前節で説明した複製方式(clone/copy)のインスタンス生成時に意味を成しており、基底プロトタイプのメンバ変数/関数を持ってきて合成するための機能になっている。前節で説明したミックスインの作法通りである。従ってparentスロットは合成ミニモジュール候補とも読み替えられる。またparentスロットがない言語では、指定インスタンスをself(これはthisやmeとも称される)にして該当メンバにアクセスできる特殊関数の用法を継承と定義しており、この場合の継承は特殊なデリゲーションと同義になっている。原型関係は言語によっては、インスタンスのスロットに指定メンバ(関数や定数)が見つからなかった際の次のサーチ先(プロトタイプ)になっている事もあり、この場合はクラスベースの継承関係と同じ働きをする。


プロトタイプベースのオブジェクトは総じて、スロットの[[可変長配列]]として実装されている。スロットとは「シンボル+コンテンツ」のペアデータである。シンボルは[[プロパティ (プログラミング)|プロパティ]]名や[[メソッド (計算機科学)|メソッド]]名を表わし、コンテンツはプロパティ値や[[コードブロック]]参照を表わす。プロパティスロットはプロトタイプ参照や[[This (プログラミング)|this]]参照(self参照)の容器にもなる。[[Self]]ではメソッドスロットがメッセージ式で送られるセレクタの受け手になる。
オブジェクトは、原型関係(typeスロット)と継承関係(parentスロット)の二系統から体系化されている。継承関係の最上位は専ら<code>Object</code>オブジェクトであり、原型関係の最上位は専ら<code>Type</code>オブジェクトである。やや分かり難くなるが、この<code>Object</code>と<code>Type</code>も継承関係で結ばれており<code>Object</code>は基底で<code>Type</code>は派生である。同時に<code>Type</code>と<code>Object</code>は原型関係でも結ばれており<code>Type</code>は原型元で<code>Object</code>は実例先である。<code>Object</code>はフレーム&スロットであり、<code>Type</code>はシンボルである。前述の通りシンボルはdefaultスロットにシンボルが入ったフレームであり、この場合のフレームをeigenclass(ownclass)などと定義している言語もある。この系統ラインの相互再帰ループがメタオブジェクトプロトコルの特徴でもある。


オブジェクトの構築は、クローン方式かエクスニヒロ方式で行われる。クローン(clone)は既存オブジェクトを複製する方式であり、複製後にプロパティ/メソッドの自由な取り付け取り外しもできる。エクスニヒロ(ex nihilo)はプロパティ無しメソッド無しの空オブジェクトを生成(または複製)する方式であり、生成後にプロパティ/メソッドの自由な取り付けができる。[[構造体]]に似た書式でプロパティ/メソッドを初期設定して生成する方式もエクスニヒロに分類されている。[[クラス (コンピュータ)|クラス]]概念がないのでテンプレート処理的なオブジェクト構築は不可であるが、[[クラスベース]]構文が導入された言語では代替的に可能にされており、それはエクスニヒロの方で解釈されている。
=== 動的関数型プログラミング ===
プロトタイプベースは、関数オブジェクトが変数オブジェクト(値オブジェクト)を引数にしてまた返り値にするといった様式に集約されるものである。値オブジェクトは[[ダックタイピング]]で動的に型判別される。引数の動的な型判別は動的な[[多重定義|関数オーバーロード]]を自然表現し、これは従来の静的な[[関数型言語]]に対するアドバンテージになった。[[型推論]]を用いる静的な関数型言語で関数オーバーロードを実装するには、記名的型付けによる”文脈”といった追加仕様が必要になるからである。関数と変数は双方ともにプロパティとメソッドを自由に付け足し付け替えできるので、関数のプロパティは[[二階述語論理]]の表現手法になり、変数のメソッドは[[高階述語論理]]の表現手法になった。プロパティとメソッドの保持構成による型判別は構造的型付けでも行われる。[[ミックスイン]]は一定の目的に基づくプロパティとメソッドのアタッチを形式化した型付けのための仕組みである。これらの特徴からプロトタイプベースOOPは、関数型プログラミングの動的拡張版と見なしてもよいものになっている。


クローンで構築されたオブジェクトのプロトタイプ-スロットには、クローン元になったオブジェクトの参照が自動代入される。プロトタイプ-スロットは再代入可能なので、エクスニヒロで構築された空オブジェクトにも、そのプロトタイプにするオブジェクト参照を自由に追加できた。プロトタイプは暗黙の[[委譲]]先になり、オブジェクトがアクセス要求されたプロパティ/メソッドを持っていない場合は、そのプロトタイプで自動サーチされる。これは[[継承 (プログラミング)|継承]]になった。
== 来歴 ==
プロトタイプベースのルーツは、1972年に[[アラン・ケイ]]が開発した言語「[[Smalltalk]]」が採用していたメタオブジェクトの仕様に求めることができる。オブジェクト指向は元々Smalltlkの設計を説明する中で初めて発信された用語である。故にプロトタイプベースは元祖[[オブジェクト指向]]と同時に誕生したスタイルと言ってよいが、Smalltalkでのメタオブジェクトはアラン・ケイが最重視していたメッセージングの仕組みを実現するためのインフラストラクチャであったので、そのスタイル自体が取り沙汰されることは無かった。それはLISP風プログラミングの拡張と見なせるものでもあった。Smalltalkのメタオブジェクト仕様は「[[LISP]]」のアトム/シンボル型/リストといった情報要素を参考にしていた。


プロトタイプベースの[[ポリモーフィズム]]は、[[クラス (コンピュータ)|クラス]]がないことから[[オーバーライド|メソッドオーバーライド]]による[[仮想関数テーブル|仮想関数]]は成立しないが、オブジェクトの{{仮リンク|構造的型付け|en|Structural type system}}解釈による[[メソッド (計算機科学)|メソッド]]の[[サブタイピング]]がそれを担っている。また、[[動的型付け]]による[[動的束縛|動的バインディング]]も用いられる。
1980年代になって知名度を得た「[[Smalltalk]]」本来の要点であるメッセージングは、その実装が途上段階でもあった理由からさほど認知される事はなく、代わりにその下部構造であるメタオブジェクト仕様の方が「[[Simula|Simula 67]]」由来の[[クラス (コンピュータ)|クラス]]と[[インスタンス]]という視点から技術的関心を集めて、[[オブジェクト指向プログラミング|オブジェクト指向]]を表舞台に立たせる原動力になっている。しかしこれは、オブジェクトに<code>subclass:</code>セレクタをメッセージングして派生クラスをクローンするという仕組みの糖衣構文的な事前定義が、そのまま額面通りにSimula風のクラス定義になぞらえられた曲解的解釈でもあった。そこでは動的な[[メタクラス|メタオブジェクト]]と静的な[[抽象データ型|ユーザー定義型]]という性質の違いは無視されていた。1980年代を通して、元祖オブジェクト指向のプロトタイプ性質はクラス機構に覆い隠される事になり、オブジェクト指向もまた元祖とは異なる[[クラス (コンピュータ)|クラス]]重視の仕様で世間に広まっている。その一方でSmalltalkや[[LISP]]コミュニティでは本来のメタオブジェクトを中心にしたパラダイムが追求されていた。


プロトタイプベースでは、[[カプセル化]]は軽視されていることが多い。
1990年に[[Smalltalk]]の方言として公開された「[[Self]]」はプロトタイプベースと定義された初のOOP言語として知られている。ただしこれは後付けの解釈であり「[[JavaScript]]」こそがプロトタイプベースの立役者とする見方もある。1990年代のプログラム言語の大衆化が重視されるようになった風潮の中で「[[Self]]」が示したメタオブジェクトないし[[メタクラス]]の考え方は、[[スクリプト言語]]や[[Webプログラミング|Webプログラミング言語]]に適した手軽で柔軟なパラダイムとして注目を集めた。1994年に「[[Python]]」のver 1.0がリリースされ、1996年に「[[JavaScript]]」「[[Ruby]]」が公開された。その公開当初から人気を集めたJavaScriptの存在感が、従来のクラス中心のオブジェクト指向とは異なることを示すための新しいスタイル定義を必要にしている。


== 脚注 ==
== 脚注 ==
{{Reflist}}
{{Reflist}}

== 参考文献 ==
* {{cite book|first=Martin|last=Abadi|author-link=Martin Abadi|author2=Luca Cardelli|author2-link=Luca Cardelli|title=A Theory of Objects|publisher=Springer-Verlag|year=1996|isbn=978-1-4612-6445-3}}
* [http://www.laputan.org/reflection/warfare.html Class Warfare: Classes vs. Prototypes], by Brian Foote.
* {{cite book|editor1-last=Noble|editor1-first=James|editor2-last=Taivalsaari|editor2-first=Antero |editor3-last=Moore|editor3-first=Ivan|year=1999|title=Prototype-Based Programming: Concepts, Languages and Applications|publisher=Springer-Verlag|isbn=981-4021-25-3}}
* [http://web.media.mit.edu/~lieber/Lieberary/OOP/Delegation/Delegation.html Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems], by Henry Lieberman, 1986.


== 関連項目 ==
== 関連項目 ==
*[[LISP]]
*[[Smalltalk]]
*{{仮リンク|Metaobject|en|Metaobject|label=}}
*{{仮リンク|Metaclass|en|Metaclass|label=}}
*[[The Art of the Metaobject Protocol|The art of the metaobject protocol]]
*[[Common Lisp]]
*[[Common Lisp Object System]]
*[[Self]]
*[[Self]]
*[[Lua]]
*[[JavaScript]]
*[[JavaScript]]
{{プログラミング言語の関連項目}}
*[[Io (プログラミング言語)|Io]]
*[[REBOL]]


{{プログラミング言語の関連項目}}
{{DEFAULTSORT:ふろとたいふへえす}}
{{DEFAULTSORT:ふろとたいふへえす}}
[[Category:オブジェクト指向]]
[[Category:オブジェクト指向]]

2022年10月23日 (日) 06:19時点における最新版

プロトタイプベース (: Prototype-based) は、オブジェクト指向プログラミング(OOP)のスタイルのひとつであり、オブジェクトの生成に既存オブジェクトの複製を用いるスタイルを指している。これには直後にメンバを拡充するための空オブジェクトの複製も含まれている。このスタイルは、インスタンスベース(Instance-based)とも呼ばれている。これと対比されるOOPスタイルにクラスベースがある。

プロトタイプベースOOPの原点はSmalltalk方言のSelfであり、Smalltalkのクラスベース設計を平易化する試みから1987年に誕生している。他にはLuaJavaScriptEtoysECMAScriptREBOLIoTypeScriptなどがある。

プロトタイプとは、複製元になったオブジェクトを意味しており、複製先のオブジェクトから見てそう呼ばれる。プロトタイプは同時にそのオブジェクトの暗黙の委譲先になり、これはプロトタイプを複製が継承していることと同じになる。

来歴

[編集]

プロトタイプベースは、Smalltalkクラスベース設計を平易化する試みから考案されたスタイルなので、Smalltalkの設計を知らないとそれが作られた理由も分からないものになる。ここではアラン・ケイが概略したSmalltalk設計の六項目を紹介して、クラスベースに関連する部分を和訳しておく[1]

1, EverythingIsAnObject.(全てはオブジェクトである)

2, Objects communicate by sending and receiving messages (in terms of objects).

3, Objects have their own memory (in terms of objects).(オブジェクトは自身の記憶を持つ)

4, Every object is an instance of a class (which must be an object).(全てのオブジェクトはクラスのインスタンスであり、クラスもまたオブジェクトである)

5, The class holds the shared behavior for its instances (in the form of objects in a program list).(クラスはそのインスタンスたちが共有する振る舞いを保持する)

6, To eval a program list, control is passed to the first object and the remainder is treated as its message.

(4)は数学的帰納な文章なので、インスタンスのひな型であるクラスもまたメタクラスのインスタンス化であり、そのメタクラスもまた他のメタクラスのインスタンス化であると解釈される。これは元祖クラスベースの大きな特徴であったが、実際はもう少し複雑でメタクラスとクラスの間に中間媒体が挟まれていた。i=インスタンス/C=クラス/M=メタクラスとするとインスタンス化の流れは、M → C class → C → iとなり、C classが中間媒体である。中間媒体は不変の静的型付け用としてオリジナルを保存し、クラスは可変の動的型付け用であった。

また、(3)で記憶(データ)はクラスとインスタンスの双方が持つが、(5)で振る舞い(メソッド)はクラスのみが保持するとされており、これも混乱を招きやすいネックになっていた。元祖クラスベース設計は理論面では美しかったが、実践面では甚だ複雑として開発陣からも不評になり、それは哲学的なインスタンス化チェーンと、クラスとインスタンスに対する振る舞いの妙に起因していた。

その改善策としての、オブジェクトからクラスインスタンスの概念を無くしてしまおうという案が、プロトタイプベースの原点になっている。この案は言語視点では、クラスを無くしてインスタンスに一元化するとも解釈できるので、インスタンスベース(Instance-based)と別称された。クラスのインスタンス化を、インスタンスのクローンに置き換えることでSmalltalkクラス設計の改善が図られ、Selfはこの案に沿って制作されたSmalltalk方言であった。ここでは(1)の遵守が第一にされていた。

なお、C++/Pythonからの静的/動的クラスベース設計では、クラスインスタンスを、型と値の役割に固定してインスタンス化の相互再帰を無くしたことから、インスタンスのみがオブジェクトになっている。メタクラスはクラス構成操作のリフレクション機能になっている。これは(1)の事実上の撤廃であった。

現在では、C++/Python発のクラスベース設計が多くの言語で採用されており、Self発のプロトタイプベースは少数派になっている。のみならずプロトタイプベースの代表格JavaScriptECMAScriptではクラスベース構文が導入されるに到っており、TypeScriptはクラスベースとプロトタイプベースの折衷にされている。プロトタイプベースはやや汎用性に欠けてオブジェクト指向の主流にはなり得なかったという結論になる。

概要

[編集]

Selfとは少し別の切り口から、Smalltalkクラスベース設計の平易化を図ったダグラス・クロックフォード英語版は、JavaScriptのプロトタイプベースをこのように概略している[2]

You make prototype objects, and then … make new instances. Objects are mutable in JavaScript, so we can augment the new instances, giving them new fields and methods. These can then act as prototypes for even newer objects. We don't need classes to make lots of similar objects… Objects inherit from objects. What could be more object oriented than that?
(あなたはプロトタイプ・オブジェクトと新しいインスタンスを作る。オブジェクトは可変であり、新フィールドと新メソッドを加えて拡充できる。これもまた新生オブジェクトのプロトタイプになる。クラスは必要ない。オブジェクトも同様に継承できるからだ。これ以上のオブジェクト指向があるだろうか?)

プロトタイプベースはプログラマに、オブジェクトをどう振る舞わせるかということのみに集中させて、オブジェクトが実際に振る舞えるかどうかの疑問を後回しにできる環境を提供する[3]。振る舞いとはメソッドである。疑問の後回しとは動的型付けダックタイピングを意味している。プロトタイプベースは静的型付けの実装を排除してる訳ではないが、その特性と利点を最大限に活かすための動的型付けが好まれている。

プロトタイプベースのオブジェクトは総じて、スロットの可変長配列として実装されている。スロットとは「シンボル+コンテンツ」のペアデータである。シンボルはプロパティ名やメソッド名を表わし、コンテンツはプロパティ値やコードブロック参照を表わす。プロパティスロットはプロトタイプ参照やthis参照(self参照)の容器にもなる。Selfではメソッドスロットがメッセージ式で送られるセレクタの受け手になる。

オブジェクトの構築は、クローン方式かエクスニヒロ方式で行われる。クローン(clone)は既存オブジェクトを複製する方式であり、複製後にプロパティ/メソッドの自由な取り付け取り外しもできる。エクスニヒロ(ex nihilo)はプロパティ無しメソッド無しの空オブジェクトを生成(または複製)する方式であり、生成後にプロパティ/メソッドの自由な取り付けができる。構造体に似た書式でプロパティ/メソッドを初期設定して生成する方式もエクスニヒロに分類されている。クラス概念がないのでテンプレート処理的なオブジェクト構築は不可であるが、クラスベース構文が導入された言語では代替的に可能にされており、それはエクスニヒロの方で解釈されている。

クローンで構築されたオブジェクトのプロトタイプ-スロットには、クローン元になったオブジェクトの参照が自動代入される。プロトタイプ-スロットは再代入可能なので、エクスニヒロで構築された空オブジェクトにも、そのプロトタイプにするオブジェクト参照を自由に追加できた。プロトタイプは暗黙の委譲先になり、オブジェクトがアクセス要求されたプロパティ/メソッドを持っていない場合は、そのプロトタイプで自動サーチされる。これは継承になった。

プロトタイプベースのポリモーフィズムは、クラスがないことからメソッドオーバーライドによる仮想関数は成立しないが、オブジェクトの構造的型付け英語版解釈によるメソッドサブタイピングがそれを担っている。また、動的型付けによる動的バインディングも用いられる。

プロトタイプベースでは、カプセル化は軽視されていることが多い。

脚注

[編集]
  1. ^ The Early History Of Smalltalk”. 2019年1月閲覧。
  2. ^ Crockford, Douglas. “Prototypal Inheritance in JavaScript”. 22 June 2021閲覧。
  3. ^ Taivalsaari, Antero; Noble, James (1998). “Thinking with prototypes”. Addendum to the 1998 proceedings of the conference on Object-oriented programming, systems, languages, and applications (Addendum) - OOPSLA '98 Addendum (New York, New York, USA: ACM Press). doi:10.1145/346852.346949. https://doi.org/10.1145/346852.346949. 

参考文献

[編集]
  • Abadi, Martin; Luca Cardelli (1996). A Theory of Objects. Springer-Verlag. ISBN 978-1-4612-6445-3 
  • Class Warfare: Classes vs. Prototypes, by Brian Foote.
  • Noble, James; Taivalsaari, Antero; Moore, Ivan, eds (1999). Prototype-Based Programming: Concepts, Languages and Applications. Springer-Verlag. ISBN 981-4021-25-3 
  • Using Prototypical Objects to Implement Shared Behavior in Object Oriented Systems, by Henry Lieberman, 1986.

関連項目

[編集]