一般化されたCLOS機能

オブジェクト指向プログラミングに何らかの形で直面し、それを理解しようとした開発者は、Common Lisp言語のオブジェクトシステムであるCLOSについて知っていたに違いありません。 「。



多くの人々は、一般化された関数は静的関数のオーバーロードに単純に似ていると信じていますが、それはダイナミクスのみです、これは完全に間違っています。

これがself / thisによるディスパッチの拡張、つまり「仮想関数」のいくつかの引数への拡張であるとさえ言うのは完全に正しいとは言えません。



もちろん、複数のディスパッチは一般化された関数の主な機能の1つですが、その本質はこれだけではなく、それほどではありません。



例で説明します-簡単な数式の計算機。



C#など、OOPの「古典的な」実装を持つ言語を使用すると、次のような結果が得られます。

interface IExpression { int Evaluate(); } class Negation : IExpression { IExpression expr; public Negation(IExpression e) { expr = e; } public int Evaluate() { return -expr.Evaluate(); } } class Addition : IExpression { IExpression left, right; public Addition(IExpression l, IExpression r) { left = l; right = r; } public int Evaluate() { return left.Evaluate() + right.Evaluate(); } } class Number : IExpression { int value; public Number(int v) { value = v; } public int Evaluate() { return value; } }
      
      







Lispのオプションはこれです:

 (defstruct (negation (:constructor negation (expr)) (:conc-name nil)) expr) (defstruct (addition (:constructor addition (left right)) (:conc-name nil)) left right) (defgeneric evaluate (expr) (:method ((expr negation)) (- (evaluate (expr expr)))) (:method ((expr addition)) (+ (evaluate (left expr)) (evaluate (right expr)))) (:method ((expr number)) expr)) ;; (evaluate (addition 5 (negation -5))) ;; ==> 10
      
      







コードのサイズを小さくすることに加えて、最初に目を引くのは、メソッドがデータ構造から分離され、一般化された関数にグループ化されることです。

2番目の顕著な違いは、そのようなインターフェイスがないことです。

さらに、数値を表すための新しいクラスやデータ構造を導入せず、単に標準クラス番号の 評価に特化しました。



一般的に、この言葉のオブジェクト指向の理解におけるインターフェイスのCLOS概念の欠如、および抽象クラスは、私の観察によれば、非常に強く、従来のオブジェクト指向言語からのコードの移植を妨げ、人々の言語とオブジェクトモデルの学習に追加の障害を置きますこれらの最も伝統的なオブジェクト指向言語に慣れている; さらに、問題はそのような人々が「一般化された関数のシグネチャの均一性の要件」を作成することです(つまり、グループ内のすべてのメソッドは同じ数の必須パラメーターを持たなければならないことを意味します)。最初の引数。 おそらく、それらの一部は、アクセス修飾子クラス、「プロパティ」、およびインターフェイスの継承と実装の分離がないために不便です。



はい、CLOSでの従来のOOPの直接投影は機能しないか、少なくともひどいように見えますが、これには非常に具体的な理由があり、それは次のとおりです。



理由により、メソッドはクラスから分離されません。 CLOSオブジェクトパラダイムは、メッセージの送信と処理(メッセージの受け渡し)に関連付けられている従来のOOPとは根本的に異なります。



CLOS は、エンティティの状態動作から、 相互および外部世界との相互作用に重点をシフトします。



上に書いたように、CLOSにはインターフェースはありません。 実際、これは完全に真実ではありません-一般化された関数インターフェースであり、それらのメソッドは実装です。



この時点で、関数型プログラミングの支持者の1人は「ファイン、まあ、これは通常のパターンマッチングとどう違うのですか?」



それは大きく異なります。 まず、一般化された関数のメソッドの特化は、値とクラスの両方で可能であり、後者の場合、継承を考慮に入れます。 第二に、一般化された関数にメソッドを追加したり、実行時に直接追加することもできます。 そして第三に、汎用関数に対していわゆる「メソッドコンビネーター」を設定することにより、ディスパッチを任意に制御できます。これは、標準コンビネーターのデコレーターの前後、前、後、および前後については言うまでもありません。



LiveJournalでメソッドコンバイナについて書いたことがあります。読みたい人は、ここで、組み込みコンビネータの1つであるprognを使用して 、通常のOO言語の「コンストラクタ」の類似性を実現する方法を示します。

 (defgeneric construct (object &key) (:method-combination progn :most-specific-last)) (defun new (class &rest args &key &allow-other-keys) (let ((object (make-instance class))) (apply #'construct object args) object)) (defclass superclass () (some-slot)) (defclass subclass (superclass) ()) (defmethod construct progn ((object superclass) &key value) (setf (slot-value object 'some-slot) value) (write-line "Superclass constructed")) (defmethod construct progn ((object subclass) &key) (write-line "Subclass constructed") (format t "Slot value: ~s~%" (slot-value object 'some-slot))) ;; (new 'subclass :value 123) ;; ==> Superclass constructed ;; Subclass constructed ;; Slot value: 123
      
      







私の意見では、CLOSオブジェクトモデルは、OOPと関数型プログラミングのベストを有機的に合成したものであり、それでも提示されているパラダイムの最も完璧な実装です。

主流派がそのアイデアを非常にゆっくりと渋々と受け入れているのは残念ですが、それをさらに悪化させています。






All Articles