(ほとんどの?いくつかの?)複数のディスパッチ言語では、プログラムの実行のある時点で各メソッドが関数に追加されることを正しく理解しました。
次に、機能としての複数のディスパッチにより、関数が変更可能になると結論付けることができますか?
すべてのメソッドが (ジェネリック) 関数に (ロード時に) 一緒にアタッチされている複数のディスパッチ言語があるので、異なる時点で異なる状態の関数を見ることができませんか?
(ほとんどの?いくつかの?)複数のディスパッチ言語では、プログラムの実行のある時点で各メソッドが関数に追加されることを正しく理解しました。
次に、機能としての複数のディスパッチにより、関数が変更可能になると結論付けることができますか?
すべてのメソッドが (ジェネリック) 関数に (ロード時に) 一緒にアタッチされている複数のディスパッチ言語があるので、異なる時点で異なる状態の関数を見ることができませんか?
プログラムの実行のある時点で。
Common Lisp では、メソッド定義が実行されるときにメソッドが追加/置換されます - コンパイルされたシステムの場合、これは通常、コンパイルされたコードのロード時に行われます - 必ずしもプログラムの実行中ではありません.
Common Lisp には、その動作によって定義されるオブジェクト システム(CLOS、Common Lisp オブジェクト システム) があることを思い出してください。言語や言語拡張機能とは少し異なります。
Common Lisp では、オブジェクト システムを実行時に変更できます。たとえば、メソッドの追加/削除/置換も。
Common Lisp は、複数の適用可能なメソッドを有効なメソッドに結合して、実行することもできます。典型的な例: 適用可能なすべてのメソッドと、最も具体的な適用可能な主要なメソッドが 1 つの有効なメソッドに結合されます。:before
一部の実装では、CLOS の拡張機能が存在します。これにより、ジェネリック関数が変更されないように封印されます。
オブジェクト システムの概念のより長い説明については、Richard P. Gabriel によるThe Structure of a Programming Language Revolution を参照してください。
Common Lisp では、仕様から次のことが読み取れます。
フォームが評価されると、次
defgeneric
の 3 つのアクションのいずれかが実行されます ( によりensure-generic-function
)。
- 指定された名前のジェネリック関数が既に存在する場合、既存のジェネリック関数オブジェクトが変更されます。現在のフォームで指定されたメソッドが追加され、以前のフォームで
defgeneric
定義された既存のジェネリック関数のメソッドは削除されます。defgeneric
現在のフォームによって追加されたメソッドは、、、、またはでdefgeneric
定義されたメソッドを置き換える可能性があります。ジェネリック関数の他のメソッドは、影響を受けたり置き換えられたりすることはありません。defmethod
defclass
define-condition
defstruct
- 指定された名前が通常の関数、マクロ、または特殊な演算子の名前である場合、エラーが通知されます。
- それ以外の場合は、フォームのメソッド定義で指定されたメソッドを使用してジェネリック関数が作成されます
defgeneric
。
メソッド定義フォームが評価されると、メソッド オブジェクトが作成され、次の 4 つのアクションのいずれかが実行されます。
- 指定された名前のジェネリック関数が既に存在し、パラメーター スペシャライザーと修飾子で新しいメソッド オブジェクトと一致するメソッド オブジェクトが既に存在する場合、新しいメソッド オブジェクトが古いメソッド オブジェクトを置き換えます。パラメータのスペシャライザと修飾子に関して別のメソッドと一致するメソッドの定義については、セクション 7.6.3 (パラメータのスペシャライザと修飾子に関する合意) を参照してください。
- 指定された名前のジェネリック関数が既に存在し、パラメーター スペシャライザーと修飾子で新しいものと一致するメソッド オブジェクトがない場合、既存のジェネリック関数オブジェクトは新しいメソッド オブジェクトを含むように変更されます。
- 指定された名前が通常の関数、マクロ、または特殊な演算子の名前である場合、エラーが通知されます。
- それ以外の場合は、メソッド定義フォームで指定されたメソッドを使用してジェネリック関数が作成されます。
function-name が、引数に異なる値を持つジェネリック関数を指定し
:lambda-list
、新しい値が既存のすべてのメソッドのラムダ リストと一致するか、メソッドがない場合、値が変更されます。そうでない場合、エラーが通知されます。function-name が
:generic-function-class
引数の値が異なるジェネリック関数を指定し、新しいジェネリック関数クラスが古いものと互換性がある場合、ジェネリック関数change-class
のクラスを変更するために呼び出されます。そうでない場合、エラーが通知されます。function-name が、引数の値が異なる汎用関数を指定している場合
:method-class
、値は変更されますが、既存のメソッドは変更されません。
と もadd-method
ありremove-method
ます。
ご覧のとおり、ジェネリック関数は、defmethod
定義間、さらにはdefgeneric
定義間でも識別を保持します。Common Lisp ではジェネリック関数は変更可能です。
Julia では、ドキュメントから以下を読むことができます。
複数のメソッドを持つ関数を定義するには、異なる数と型の引数を使用して関数を複数回定義するだけです。関数の最初のメソッド定義は関数オブジェクトを作成し、後続のメソッド定義は新しいメソッドを既存の関数オブジェクトに追加します。
ご覧のとおり、関数オブジェクトは Julia で変更可能です。
これは、他のすべての複数ディスパッチ言語については何も言いません。たとえば、メソッドを追加すると、前の関数に似た新しい関数が返されますが、メソッドが追加されます。または、関数がコンパイル時に静的に生成される言語で、実行時に変更することはできず、メソッドを追加または削除することさえできません。
これに関する素晴らしいセクションがある優れた「 Getting started with Julia」本からの言い換え(私の強調):
関数は本質的にジェネリックとして定義されていること、つまり、さまざまなタイプの引数に使用できることは既に説明しました。コンパイラは、関数が新しい型の引数で呼び出されるたびに、別のバージョンの関数を生成します。引数の型の特定の組み合わせに対する関数の具体的なバージョンは、Juliaではメソッドと呼ばれます。関数の新しいメソッドを定義するには (オーバーロードとも呼ばれます)、関数名は同じですが、シグネチャが異なります。つまり、引数の型が異なります。
すべてのメソッドのリストは、関数自体の仮想メソッド テーブル (vtable) に格納されます。メソッドは特定のタイプに属しません。関数が呼び出されると、Julia は実行時にその vtable でルックアップを行い、すべての引数の型に基づいてどの具体的なメソッドを呼び出す必要があるかを見つけます。これは Julia の複数ディスパッチのメカニズムであり、Python も C++ も Fortran も実装していません。これにより、通常のオブジェクト指向のコードでは既存のクラスのクラスまたはサブクラスを変更し、ライブラリを変更する必要があるオープンな拡張が可能になります。複数のディスパッチでは、位置引数のみが考慮され、キーワード引数は考慮されないことに注意してください。
これらのさまざまな方法のそれぞれについて、プロセッサの命令セットを対象とした特殊な低レベル コードが生成されます。オブジェクト指向 (OO) 言語とは対照的に、vtable は型 (またはクラス) ではなく関数に格納されます。OO 言語では、メソッドは object.method() という単一のオブジェクトに対して呼び出されます。これは一般に、シングル ディスパッチと呼ばれます。Julia では、関数が複数の型に属している、または関数がさまざまな型に特化またはオーバーロードされていると言えます。高レベルの動的言語のように読めるコードを、ほぼ完全に C のように機能するマシン コードにコンパイルする Julia の機能は、複数のディスパッチを実行する機能に由来しています。
だから、私がこれを理解する方法(間違っているかもしれません)は次のとおりです。
methods()
(ただし、その関数名で実行する場合、これは明示的なメソッドとして表示されません)これで関数が変更可能になるとは言いませんが、それはまったく別の問題です。isimmutable()
関数「ハンドル」の関数を使用して、それらが不変であることを確認できます。
*モジュールをプリコンパイルできることは知っていますが、これらのオンザフライでコンパイルされたバージョンがセッション間で何らかの形式で保存されるかどうかは完全にはわかりません-コメントを歓迎します:)
動的性は、デバッグのみの場合でも、アプリケーションの実際の資産になる可能性があります。関数が後で更新されたり、再定義されたりするのを防ごうとするのは、少し近視眼的かもしれません。しかし、静的ディスパッチが確実に必要な場合は、標準の一部ではありませんが、依然として大部分がサポートされている MOP (Meta-Object Protocol) のおかげで、ジェネリック関数の独自のクラスを定義できます。これがInlined-Generic-Functionライブラリが提供するものです (これは、CLOS が拡張に対してオープンであるため可能です)。