手動メモリ管理またはランタイムガベージコレクションなしのタイプベースのメモリの安全性?


13

HaskellやIdrisのような、ガベージコレクションなしのシステムプログラミングを目的とし、ランタイム(または少なくともCとRustの「ランタイム」)を持たないタイプフルで純粋な関数型プログラミング言語が必要だったとします。多少なりとも、ベアメタル上で実行できるもの。

手動メモリ管理やランタイムガベージコレクションを必要としない静的メモリの安全性のオプションにはどのようなものがありますか?また、HaskellやIdrisに類似した純粋な関数の型システムを使用して問題を解決するにはどうすればよいですか?


ガベージコレクションを回避する方法として、言語の型を使用したいということですか?基本的な問題は、関数の評価で発生します。関数は、現在のランタイム環境をカプセル化するクロージャーに対して評価されます。これが、ガベージコレクションを行わなければならない主な原因です。関数の型付け規則を変更しない限り、型がこれにどのように役立つかわかりません。Javaやその他の壊れた -abstractions の言語は、クロージャーの形成を変異させることでこれを回避します:ガベージコレクションを必要とする参照を許可しません。λ
アンドレイバウアー

Rustは、その所有モデルとボローチェッカーを使用して、関数の評価とクロージャの同じ問題に対処する必要がありましたか?メモリ管理とは、値が生きている時間、他の値がそれらに依存していることを知り、使用されていない値が無効になったときに破棄することを意味します。だから私は本当に新しい所有権システムと「借入チェッカー」(これはRustの方法です)。
チェイス

マーティン・ホフマンのLFPLはどうですか?特別な基本型である「ダイヤモンド」があり、その上で線形型規則が適用され、型が基本的なメモリ使用量(割り当て/割り当て解除)を考慮できるようになります。それはあなたが話している方向に行きますか?
ダミアーノマッツァ

回答:


18

大まかに言って、安全な手動メモリ管理には2つの主な戦略があります。

  1. 最初のアプローチは、線形論理などの部分構造論理を使用してリソースの使用を制御することです。この考え方は基本的に線形論理の始まりから浮かんできており、基本的には収縮の構造規則を禁止することで、すべての変数が一度しか使用されないため、エイリアシングがないという観察に基づいています。その結果、インプレース更新と再割り当ての違いはプログラムからは見えないため、手動のメモリ管理で言語を実装できます。

    これがRustの動作です(アフィン型システムを使用します)。Rustスタイル言語の理論に興味がある場合、読むのに最適な論文の1つはAhmed et alのL3:A Linear Language with Locationsです。余談ですが、ダミアーノ・マッツァが述べたLFPL計算は線形であり、RAML言語でそれから派生した完全な言語を持っています

    Idrisスタイルの検証に興味がある場合は、Xi et alのATS言語を参照してください。これは、Haskellスタイルのインデックス付き型に基づく検証をサポートするRust / L3スタイルの言語で、パフォーマンスを制御します。

    さらに積極的に依存するアプローチは、Microsoft Researchで開発された完全な依存型理論であるF-star言語です。この言語には、Nanevski et alのHoare Type Theory(または、独自の 線形および従属型の統合)の精神で、事前条件と事後条件を備えたモナドインターフェイスがあり、低レベルCコードにコンパイルできる定義済みのサブセットがあります-実際、彼らはすでにFirefoxの一部として検証済みの暗号コードを出荷しています!

    明確にするために、F-starもHTTも線形型言語ではありませんが、モナドのインデックス言語は通常、レイノルドとオハーンの分離論理に基づいています。これは、ポインタープログラムのHoareロジックのアサーション言語。

  2. 2番目のアプローチは、アセンブリ(または必要な低レベルIR)の動作を単純に指定し、線形または分離ロジックの形式を使用して、プルーフアシスタントでの動作を直接推論します。基本的に、正しいプログラムを生成するだけの非常に派手なマクロアセンブラーとして、証明アシスタントまたは依存型付き言語を使用できます。

    Jensenらの低レベルコード用高レベル分離ロジックは、これの特に純粋な例です。x86アセンブリ用の分離ロジックを構築します。しかし、プリンストンの検証済みソフトウェアツールチェーンやイェールのCertiKOSプロジェクトのように、この流れには多くのプロジェクトがあります

変数の使用を制限することで所有権を追跡することがすべての鍵であるため、これらのアプローチはすべてRustのように「感じ」ます。


3

線形型と分離ロジックはどちらも優れていますが、プログラマーの多大な努力が必要になる場合があります。たとえば、Rustで安全なリンクリストを作成するのは非常に困難です。

しかし、それほど厳密な保証はありませんが、プログラマの労力がはるかに少ない代替手段があります。(かなり古い)作業の流れは、領域(通常はスタック)を使用してメモリの安全性を保証することです。コンパイラは、領域推論を使用して、割り当てられたデータがどの領域に入るかを静的に決定し、範囲外になると領域の割り当てを解除できます。

領域の推論は、確実に安全で(到達可能なメモリの割り当てを解除できない)、プログラマの干渉は最小限で済みますが、「合計」ではありません(つまり、「何もしない」よりも間違いなくはるかに優れていますが、メモリをリークする可能性があります)。実際のGC。のMLtonMLキットコンパイラは、領域を使用してほとんどのGC呼び出しを排除しますが、それ以外の場合はメモリリークが発生するため、GCを保持しています。地域に関する初期の先駆者の何人かによると、地域推論は実際にはこの目的のために考案されたものではありません(自動並列化のためだと思います)。しかし、メモリ管理にも使用できることが判明しました。

出発点として、Mads TofteとJean-Pierre Talpinによる論文「Region of Regionsを使用したTyped Call-by-Valueλ-calculusの実装」を参照してください。地域推論に関する他の論文については、M。TofteとJ.-P.による他の論文を探してください。Talpin、Pierre Jouvelotの作品、Greg Morrisett、Mike Hicks、Dan GrossmanのCycloneに関する一連の論文。


-2

「ベアメタル」システムの簡単なスキームは、すべてのランタイムメモリ割り当てを単純に禁止することです。C malloc/freeペアでもランタイムライブラリが必要であることを忘れないでください。ただし、すべてのオブジェクトがコンパイル時に定義されている場合でも、タイプセーフな方法で定義できます。

ここでの主な問題は、プログラムの実行中に作成される純粋な関数型言語の不変の値のフィクションです。実際のハードウェア(および確かにベアメタルシステム)は、供給が限られている可変RAMに依存しています。実際の関数型言語実装のランタイムは、新しい「不変の」値が作成されるとRAMを動的に割り当て、「不変の」値が不要になったときにそれらをガベージコレクションします。

また、最も興味深い問題の場合、少なくともいくつかの値の有効期間はランタイム(ユーザー)入力に依存するため、有効期間を静的に決定することはできません。ただし、ライフタイムが入力に依存していなくても、非常に重要です。単純にすべての数を順番にチェックし、すべての素数をチェックするだけで、素数を繰り返し検索するプログラムを実行しsqrt(N)ます。明らかに、この必要性は素数を保持し、非素数に使用されるメモリをリサイクルできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.