スタックセマンティクスの使用を許可する関数型言語はありますか?スコープの終わりでの自動決定論的破壊?
スタックセマンティクスの使用を許可する関数型言語はありますか?スコープの終わりでの自動決定論的破壊?
回答:
関数型プログラミングの専門家ではありませんが、私が知っていることではありません。
関数から返される値には、同じ関数内で(スタック上に)作成された他の値への参照が含まれたり、パラメーターとして渡されたり、渡された何かによって参照されたりする可能性があるため、原則としてかなり難しいようですパラメータとして。Cでは、プログラマーが物事を正しく行わなかった場合に、ぶら下がりポインター(より正確には、未定義の動作)が発生する可能性を許容することで、この問題に対処しています。関数型言語のデザイナーが承認するような種類のソリューションではありません。
ただし、潜在的な解決策があります。1つの考えは、値の有効期間を値への参照とともに値の型の一部にし、スタックに割り当てられた値が返されたり、返されたものによって参照されたりするのを防ぐ型ベースのルールを定義することです関数。私はその意味を理解していませんが、恐ろしいことだと思います。
モナドコードの場合、(実際にまたはほぼ)モナドでもある別の解決策があり、一種の自動的に決定論的に破壊されたIORefを与えることができます。原則は、「ネスト」アクションを定義することです。(結合演算子を使用して)組み合わせた場合、これらはネスト制御フローを定義します-「XML要素」と思います。左端の値が外側の開始タグと終了タグのペアを提供します。これらの「XMLタグ」は、抽象化の別のレベルでモナドアクションの順序を定義しているだけです。
ある時点(連想構成のチェーンの右側)で、ネストを終了するために何らかの種類のターミネーターが必要です-中央の穴を埋める何か。ターミネータの必要性は、おそらくネスト構成演算子がモナドではないことを意味しますが、詳細についてはまだ作業していないので完全にはわかりません。ターミネータを適用することはすべて、ネストアクションを効果的に合成された通常のモナドアクションに変換するだけなので、そうではないかもしれません-ネスト構成演算子に必ずしも影響を与えるわけではありません。
これらの特別なアクションの多くは、ヌルの「終了タグ」ステップを持ち、「開始タグ」ステップをいくつかの単純なモナドアクションと同一視します。ただし、変数宣言を表すものもあります。これらは、開始タグを持つコンストラクターと終了タグを持つデストラクタを表します。あなたは次のようなものを手に入れます...
act = terminate ((def-var "hello" ) >>>= \h ->
(def-var " world") >>>= \w ->
(use-val ((get h) ++ (get w)))
)
次の実行順序でモナド構成に変換すると、各タグ(要素ではない)が通常のモナドアクションになります...
<def-var val="hello"> -- construction
<def-var val=" world> -- construction
<use-val ...>
<terminator/>
</use-val> -- do nothing
</def-val> -- destruction
</def-val> -- destruction
このようなルールにより、C ++スタイルのRAIIを実装できます。IORefのような参照は、通常のIORefがモナドをエスケープできない理由と同様の理由で、スコープをエスケープできません-連想構成の規則は、リファレンスがエスケープする方法を提供しません。
編集 -私はほとんど言うのを忘れていました-ここには定かでない明確な領域があります。基本的に、外部変数が内部変数を参照できないようにすることが重要です。したがって、これらのIORefのような参照でできることには制限が必要です。繰り返しますが、私はすべての詳細を処理していません。
したがって、構築により、たとえば、破壊が終了するファイルを開くことができます。建設は、破壊を閉じるソケットを開く可能性があります。基本的に、C ++と同様に、変数はリソースマネージャーになります。ただし、C ++とは異なり、自動的に破棄できないヒープ割り当てオブジェクトはありません。
この構造はRAIIをサポートしていますが、ガベージコレクターが必要です。ネストアクションはメモリを割り当てて解放し、リソースとして扱いますが、メモリのチャンク内や他の場所に(潜在的に共有される)機能値へのすべての参照があります。メモリを単純にスタックに割り当てることができ、ヒープを解放する必要がないため、実際の重要性は(ある場合)他の種類のリソース管理にあります。
そのため、少なくともRAIIが単純な入れ子スコープに基づいている場合は、RAIIスタイルのリソース管理をメモリ管理から分離することができます。メモリ管理にはガベージコレクタが必要ですが、他のリソースの安全かつタイムリーな自動確定的クリーンアップを取得します。
shared_ptr<>
)が、決定論的な破壊を維持します。RAIIにとって難しいことの1つは、循環参照です。所有権グラフが有向非巡回グラフである場合、RAIIは正常に機能します。
C ++を関数型言語(ラムダを持つ)と見なす場合、ガベージコレクションを使用しない言語の例です。
この質問は、「関数型言語」の標準コレクションがあると想定しているため、少し不明確だと言わざるを得ません。ほとんどすべてのプログラミング言語は、ある程度の関数型プログラミングをサポートしています。そして、ほとんどすべてのプログラミング言語は、ある程度の命令型プログラミングをサポートしています。文化的な偏見や大衆の教義に導かれる以外に、どちらが機能的な言語であり、どれが必須の言語であるかを言うために、どこで線を引きますか?
質問を表現するより良い方法は、「スタックに割り当てられたメモリで関数型プログラミングをサポートすることは可能でしょうか」です。すでに述べたように、答えは非常に困難です。関数型プログラミングスタイルは、再帰的なデータ構造の割り当てを自由に促進します。これには、ヒープメモリ(ガベージコレクションまたは参照カウント)が必要です。ただし、領域ベースのメモリ分析と呼ばれる非常に高度なコンパイラ分析手法があります。これを使用して、コンパイラは、スタック割り当てと同様の方法で、ヒープを自動的に割り当ておよび割り当て解除できる大きなブロックに分割できます。ウィキペディアのページには、「関数型」言語と「命令型」言語の両方に対するこの手法のさまざまな実装がリストされています。