すべての関数型言語はガベージコレクションを使用しますか?


32

スタックセマンティクスの使用を許可する関数型言語はありますか?スコープの終わりでの自動決定論的破壊?


確定的破壊は、実際には副作用がある場合にのみ役立ちます。純粋な関数型プログラミングのコンテキストでは、それは特定の(単項の)アクションが常にシーケンスの最後に実行されるようにすることを意味します。余談ですが、ガベージコレクションを必要としない関数型の連結言語を書くのは簡単です。
ジョンパーディ

私は質問の内容に興味があります、他の人とどう関係しましたか?
マッテンツ

1
ガベージコレクションのない関数型言語では、不変のデータ構造を構造的に共有する方法がわかりません。このような言語を作成することは可能かもしれませんが、私が使用する言語ではありません。
dan_waterworth

Rustには、一般に「機能的」と特定された多くの機能があります(少なくとも、機能以外の言語では機能的機能として一般的に望まれています)。何が足りないのか興味があります。デフォルトでは、Immut、クロージャ、関数渡し、原則的なオーバーロード、ADT(まだGADTはありません)、パターンマッチング、すべてGCなし。ほかに何か?
ノエイン

回答:


10

関数型プログラミングの専門家ではありませんが、私が知っていることではありません。

関数から返される値には、同じ関数内で(スタック上に)作成された他の値への参照が含まれたり、パラメーターとして渡されたり、渡された何かによって参照されたりする可能性があるため、原則としてかなり難しいようですパラメータとして。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スタイルのリソース管理をメモリ管理から分離することができます。メモリ管理にはガベージコレクタが必要ですが、他のリソースの安全かつタイムリーな自動確定的クリーンアップを取得します。


すべての関数型言語でGCが必要な理由がわかりません。C ++スタイルのRAIIフレームワークが用意されている場合、コンパイラはそのメカニズムも使用できます。共有値はRAIIフレームワークでは問題ありません(C ++を参照shared_ptr<>)が、決定論的な破壊を維持します。RAIIにとって難しいことの1つは、循環参照です。所有権グラフが有向非巡回グラフである場合、RAIIは正常に機能します。
MSalters

実は、関数型プログラミングスタイルは、クロージャ/ラムダ/匿名関数を中心に実際に構築されています。GCがないと、クロージャを使用する自由が同じではないため、言語の機能が大幅に低下します。
来るストーム

@comingstorm-C ++にはラムダ(C ++ 11以降)がありますが、標準のガベージコレクタはありません。ラムダはクロージャ内の環境も保持します。その環境内の要素は参照によって渡される場合があり、ポインタが値によって渡される可能性もあります。しかし、2番目の段落で書いたように、C ++ではポインターがぶら下がる可能性があります-コンパイラーまたはランタイム環境ではなくプログラマーが、決して起こらないようにする責任があります。
Steve314

@MSalters-参照サイクルを作成できないようにするためのコストがかかります。したがって、言語にその制限の責任を負わせることは、少なくとも重要なことです。ポインターへの割り当ては、おそらく非一定時間の操作になります。ただし、場合によってはそれが最良のオプションかもしれません。ガベージコレクションは、さまざまなコストでその問題を回避します。プログラマーに責任を持たせることは別です。関数型言語ではなく命令型言語でダングリングポインターがOKである理由は特にありませんが、ポインターダングリングハスケルを記述することはお勧めしません。
Steve314

手動のメモリ管理は、LispクロージャーやHaskellクロージャーと同じようにC ++ 11クロージャーを使用する自由がないことを意味すると主張します。(私は実際にこのトレードオフの詳細を理解することに非常に興味があります。関数型システムのプログラミング言語を書きたいので...)
comingstorm

3

C ++を関数型言語(ラムダを持つ)と見なす場合、ガベージコレクションを使用しない言語の例です。


8
C ++を関数型言語と見なさない場合(IMHOではありません。関数型プログラムを書くことはできますが、非常に非機能的な(関数型...)プログラムを書くこともできます)
マッテンツ

@mattnzその後、答えは当てはまらないと思います。私は確かに(たとえば、ハスケルのためのような)他の言語では何が起こるかないよ
BЈовић

9
C ++が機能的であると言うことは、Perlはオブジェクト指向であると言っているようなものです
動的

少なくともc ++コンパイラは副作用をチェックできます。(const経由)
-tp1

@ tp1-(1)これが誰の言語が最適かを回帰しないことを望みます。(2)それは本当ではありません。まず、本当に重要な効果はほとんどがI / Oです。第二に、可変メモリに対する効果であっても、constはそれらをブロックしません。型システムを破壊する可能性がないと仮定しても(C ++では一般に合理的です)、論理的な一貫性と「可変」C ++キーワードの問題があります。基本的に、constにもかかわらず、まだ突然変異を持っている可能性があります。結果が「論理的に」同じであることを保証することが期待されますが、必ずしも同じ表現ではありません。
Steve314

2

この質問は、「関数型言語」の標準コレクションがあると想定しているため、少し不明確だと言わざるを得ません。ほとんどすべてのプログラミング言語は、ある程度の関数型プログラミングをサポートしています。そして、ほとんどすべてのプログラミング言語は、ある程度の命令型プログラミングをサポートしています。文化的な偏見や大衆の教義に導かれる以外に、どちらが機能的な言語であり、どれが必須の言語であるかを言うために、どこで線を引きますか?

質問を表現するより良い方法は、「スタックに割り当てられたメモリで関数型プログラミングをサポートすることは可能でしょうか」です。すでに述べたように、答えは非常に困難です。関数型プログラミングスタイルは、再帰的なデータ構造の割り当てを自由に促進します。これには、ヒープメモリ(ガベージコレクションまたは参照カウント)が必要です。ただし、領域ベースのメモリ分析と呼ばれる非常に高度なコンパイラ分析手法があります。これを使用して、コンパイラは、スタック割り当てと同様の方法で、ヒープを自動的に割り当ておよび割り当て解除できる大きなブロックに分割できます。ウィキペディアのページには、「関数型」言語と「命令型」言語の両方に対するこの手法のさまざまな実装がリストされています。


1
IMOの参照カウントガベージコレクションであり、ヒープがあるからといって、これらが唯一のオプションであることを意味するわけではありません。Cはヒープを使用してmallocとmfreeを実行しますが、(標準の)ガベージコレクタはなく、そのためのコードを記述した場合は参照カウントのみがあります。C ++はほぼ同じです-(C ++ 11では標準として)参照カウントが組み込まれたスマートポインターがありますが、本当に必要な場合は手動で新規作成して削除できます。
Steve314

参照カウントはガベージコレクションではないと主張する一般的な理由は、参照サイクルの収集に失敗することです。これは確かに単純な実装(おそらくC ++スマートポインターを含む-私はチェックしていません)に適用されますが、常にそうとは限りません。少なくとも1つのJava仮想マシン(IBM、IIRCによる)は、参照カウントをガベージコレクションの基礎として使用しました。
Steve314
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.