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