Eiffel

Eiffelは頑健なソフトウェアの生産に注力したオブジェクト指向プログラミング言語である。

1. 概要
-----

1985年にバートランド・メイヤーによって考案された。その文法はPascalを連想させるものである。Eiffelは静的な型指定を強く指向しており、かつ動的なメモリ管理を備えている。

少し前、オブジェクト指向の教科書的言語といえば、SmalltalkかEiffelか、という状況で、手続き型言語でのPascalのような存在であった。多重継承、ガベージコレクションといった特徴があるが、設計者によってライブラリのメンテナンスが重視されており、契約による設計の概念が全面に打ち出されている。同じくオブジェクト指向を取り入れた言語であるJavaほどは普及していない。Assertionなど、Javaが追いかけているところもあるが、インタフェースによる継承、GCあり、とかぶるところが多い。

また、C/C++のようにネイティブコードを直接生成するのではなく、C言語やJavaのコードを生成する、という特徴ももっている。

言語名の由来は、エッフェル塔ではなく、その設計者ギュスターヴ・エッフェルである。

2. 基本構成
-------

Eiffel のソースは以下のように記述する。 -- コメント class クラス名

inherit 継承元のクラス

creation コンストラクタの宣言

feature{アクセス権限} メンバ変数、メンバ関数の記述

end<WtXmlEndTag /> Eiffel は「クラスとはオブジェクトの生成機である」という考え方が徹底しており、このため両者の概念を混同するようなクラス変数やクラスメソッドの機能は存在しない。このことは「クラスもオブジェクトの一種である」と考える Smalltalk とは対照的である。

また「クラス」に対する考え方も独特で、例えば Java ではソースファイルをコンパイルすると「クラスファイル」というファイルを作るのを見てわかるように、一般的には「ソースコード」は「クラスの設計図」という概念であるのに対し、Eiffel では「クラス」とは「ソースコードそのものである」という考え方である。コンパイルして生成されるファイルは「クラスによって作られたインスタンス」という考え方であり、このため Eiffel ではメインルーチンに相当する処理をコンストラクタで行う。

コンストラクタはcreation下で宣言されたメンバ関数が使用される、このメンバ関数はどんな名称でも構わないが慣例的にmakeとする場合が多い。作成したクラスを使用する場合は x : !!x. でインスタンスを作成する。

クラスの継承を省略した場合は ANY というクラスの継承クラスとして扱われる。入出力などのグローバルな機能は ANY クラス内で定義されており、各 Eiffel のクラスではこれらの機能の実装に関して考慮をする必要性がないようになっている。

なお、Eiffelはクラス名は全て大文字で書かなければならないという命名規則がある。

3. rename と redefine
--------------------

Eiffel の特徴の一つとして、継承における細かい指定が可能ということが挙げられる。例えば class A feature method1 is io.put_string io.new_line end end class B inherit A feature method1 is io.put_string io.new_line end end というコードがあるとする。これは A というクラスを継承した B というクラスに同じ名前のメソッドがある場合である。このとき class C creation make feature a : A b : B

make is do !!b a := b b.method1 a.method1 end end<WtXmlEndTag /> というコードを実行したとき。b.method1 はおそらく B で定義されたメソッドが動くと誰もが期待するだろうが、a.method1 で実行されるメソッドは A と B どちらで定義されたものだろうか?この場合の処理は言語によって異なっており例えばC++の場合は A で定義されたメソッドが実行され、Javaの場合は B のメソッドが実行される。すなわち言語によって動作が違うため作成者の勘違いなどによって混乱を招くおそれがある。

Eiffelでは実のところ、どちらが実行されるか以前に上記のような例ではコンパイルすることができないことになっている。すなわち Eiffelのメソッドは全てデフォルトでは継承不可である。しかしこれでは、あまりに制約が強すぎるため、同名のメソッドを定義する必要性があるときは、継承クラスの作成者がどちらを実行するかを指定することが出来る。

具体的には rename と redefine という機能があり、上記の例では A で定義されたメソッドを実行したい場合は class B inherit A rename method1 as method2 end とする。この場合クラス B では A のメソッドを method2 という名前に改名させて、名前の衝突を防いでいる。B のインスタンスでも、クラス A のインスタンスとして扱われた場合は A の method1 が実行され、クラス B のインスタンスとして扱われた場合は B のmethod1 が実行される。B のインスタンスとして扱われた状態で A の method1 を実行するには上記の例では b.method2 とすればよい。これらの改名による効果は継承先と継承元の他、多重継承時のメソッド同士の衝突にも使用できる。

逆に A のインスタンスとして扱われる場合でも B の method1 を実行したい場合は以下のようにする。 class B inherit A redefine method1 end この場合、クラス B はクラス A の method1 を継承時に破棄しクラス B で再定義することを示している。クラス B のインスタンスは A、B どちらで扱われても method1 はクラス B のものが実行され、クラス A の method1 はもはや実行されることは無い。なお再定義を宣言された method1 は必ずクラス B で再定義しなければならず、再定義していない場合はコンパイルエラーとなる。

このように、Eiffel ではクラスの継承時に継承クラスの作成者がメソッドの扱いを自由に設定することができる。

4. コンストラクタの非継承
--------------

Eiffel の特徴の一つとして多重継承をサポートしていることが挙げられる。

多重継承時に問題となるものの一つとして、継承した二つののクラスのどちらにもコンストラクタが定義されている場合、二つのクラスのどちらのコンストラクタが実行されるか?というものがある。Eiffel の他に多重定義をサポートしている C++や Python では継承元のコンストラクタの実行順に複雑な規則が導入されている。

Eiffelではコンストラクタは継承されないことになっている。このためコンストラクタの実行手順といったものは原理的に存在しない。どうしても継承元のコンストラクタを使用する必要がある場合は、継承時に rename でコンストラクタとして宣言されているメソッドを改名し、改名したメソッドをコンストラクタ内で実行することで実装できる。

5. 脚注
-----

6. 外部リンク
--------

[1]
[2] - SmartEiffel 配布ページ