コンパイラを関数型言語で書く方が簡単なのはなぜですか?[閉まっている]


88

私はこの質問について長い間考えていましたが、Googleでの答えもStackoverflowでの同様の質問も実際には見つかりませんでした。重複している場合は、ごめんなさい。

多くの人は、OCamlやHaskellなどの関数型言語でコンパイラやその他の言語ツールを書く方が、命令型言語で書くよりもはるかに効率的で簡単だと言っているようです。

これは本当ですか?そして、もしそうなら、なぜCのような命令型言語ではなく関数型言語で書くのが、なぜそんなに効率的で簡単なのでしょうか?また、関数型言語の言語ツールは、Cのような低水準言語よりも遅くありませんか?


5
簡単だとは思いません。ただし、構文解析などのコンパイルタスクの機能的な性質は、おそらく関数型プログラミングに非常に適しています。OCamlのような関数型言語は、Cの速さに匹敵、非常に速いことができる
ロバート・ハーヴェイ

17
皆さん、これは本当に議論の余地がありますか?確かに誰かが洞察力を持っています。自分のことを知りたい。
Robert Harvey、

命令型言語よりも関数型言語を使用する理由は少なくともいくつかあるはずです。基本的に、関数型言語には副作用などがないという記事がいくつか見つかりました。しかし、それはまったく明確ではありませんでした。ただし、これが議論の余地がある場合は、それを閉じるか、質問を再定式化することをお勧めします。
2010年

12
一部のニッチが特定の言語のスタイルにより適していることを認めることは本当に議論の余地がありますか?「デバイスドライバーを書くためにJavascriptよりもCが優れている理由」は議論の余地がないと思います...
CA McCann 2010年

1
逆になると思いました。私は「超小型コンパイラ」を読んでいて、至る所で可変突然変異を使用しています。
Rolf

回答:


101

多くの場合、コンパイラーはツリーを多く使用します。ソースコードが構文ツリーに解析されます。次に、そのツリーは、タイプチェックを実行するためにタイプアノテーションを持つ別のツリーに変換されます。次に、そのツリーをコア言語要素のみを含むツリーに変換します(構文的な砂糖のような表記を砂糖を含まない形式に変換します)。これで、基本的にツリーの変換であるさまざまな最適化を実行できます。その後、通常の形式でツリーを作成し、そのツリーを反復処理してターゲット(アセンブリ)コードを作成します。

関数型言語には、パターンマッチングや効率的な再帰のサポートなどの機能があり、ツリーを簡単に操作できるため、コンパイラを書くのに一般的に優れた言語と見なされています。


これまでで最も完全な回答です。これを承認済みの回答としてマークしますが、ピート・カーカムの回答も良いと思います。
wvd

1
「正しさの証明」についてはどうでしょう。コンパイラの正しさは重要な属性であるため、関数型言語のファンは、正しさの「証明」を何らかの形でワークフローに組み込んでいるとよく耳にします。これが実際に何を意味するのかはわかりませんが、コンパイラの信頼性は重要なので、これは価値があるようです。
ウォーレンP

3
@WarrenP:「プルーフキャリーコード」の概念は、静的型付けの関数型言語から来ています。型システムを使用して、関数が正しい場合にのみ型チェックできるようにするという考え方です。コードがコンパイルされるという事実は、正しいことの証明です。もちろん、これは、言語を完全なものにし、型チェックを決定可能にしたままでは、完全には不可能です。しかし、型システムが強いほど、その目標に近づくことができます。
sepp2k 2010年

3
この概念が機能的なコミュニティで主に人気があるのは、状態が変更可能な言語では、型の状態変化がいつどこで発生するかについての情報もエンコードする必要があるためです。関数の結果がその引数にのみ依存することがわかっている言語では、型の証明をエンコードする方がはるかに簡単です(どのグローバルな状態であるかを考慮する必要がないため、コードの正確性を手動で証明する方がはるかに簡単です)可能であり、それが関数の動作にどのように影響するか)。ただし、これは特にコンパイラとは関係ありません。
sepp2k 2010年

3
私の意見では、最も重要な機能の1つはパターンマッチングです。パターンマッチングを使用した抽象構文ツリーの最適化は、非常に簡単です。パターンマッチングなしでそれを行うことは、しばしばイライラするほど難しいです。
ボブ・アマン

38

多くのコンパイラタスクは、ツリー構造でのパターンマッチングです。

OCamlとHaskellはどちらも、強力で簡潔なパターンマッチング機能を備えています。

パターンを照合するために評価または抽出される値には副作用がないため、命令型言語にパターンマッチングを追加することは困難です。


合理的な答えのように聞こえますが、これは唯一のものですか?例えば、末尾再帰のようなものも役割を果たすでしょうか?
wvd

これは、実際の実行モデルよりも型システムの問題であることを示しているようです。構造型に対して不変の値を使用した命令型プログラミングに基づくものは問題ないかもしれません。
ドナルフェロー、

@wvd:末尾再帰の最適化は、言語の機能そのものではなく、実装の詳細であり、線形再帰関数を反復ループと同等にします。Cでリンクされたリストをウォークする再帰関数は、Schemeでリストを再帰するのと同じくらい、その恩恵を受けます。
CAマッキャン

@wvd gccのCは、テール呼び出しの削除を持っている、などの他の可変状態の言語を行う
ピートKirkham

3
@camccann:言語標準がtcoを保証する場合(または、少なくとも特定の形式の再帰関数がスタックオーバーフローやメモリ消費の線形増加を引き起こさないことを保証する場合)、その言語機能を検討します。標準で保証されていなくても、コンパイラが保証している場合、それはコンパイラの機能です。
sepp2k

15

考慮すべき重要な要素の1つは、コンパイラプロジェクトの大部分は、コンパイラをセルフホストして「自分のドッグフードを食べる」ことができることです。このため、言語研究用に設計されたOCamlなどの言語を見ると、コンパイラタイプの問題に優れた機能を備えている傾向があります。

私の最後のコンパイラー風のジョブでは、Cコードを操作する際にまさにこの理由でOCamlを使用しました。これは、タスクに最適なツールでした。INRIAの人々がさまざまな優先順位でOCamlを構築していたとしたら、それほど適切ではなかったかもしれません。

とはいえ、関数型言語はあらゆる問題を解決するための最良のツールであるため、論理的には、この特定の問題を解決するための最良のツールであると言えます。QED。

/ me:Javaタスクにクロールバックして少し楽しくない...


4
-1は「関数型言語はあらゆる問題を解決するための最良のツールです。」これが本当なら、どこでもそれらを使用しているでしょう。;)
Andrei Krotkov、

15
@Andrei Krotkov:今日の今日の言葉は有名です発音:\ fə-ˈsē-shəs \機能:形容詞語源:中部フランス語facetieux、facetie jest、ラテン語facetia日付:1599 1:冗談や不適切な表現:わんぱくな<ファセットになっているだけ> 2:ユーモラスまたは面白いことを意味している:深刻ではない<ファセットになっている発言>同義語は機知に富んでいる あなたはすべての人々が合理的な俳優であり、私が恐れていると仮定しているのは、公正な仮定ではありません。
ウッコ

5
私は冗談を逃したと思います。実際の人は、真面目なことを除いて、正確なことを言っているのを知っているからです。ポーの法則だと思います。tvtropes.org/pmwiki/pmwiki.php/Main/PoesLaw
Andrei Krotkov

13
@Andrei:あなたの口論的な広告のポピュラムを使用する:「理由が感情的な無知よりも優れている場合、私たちはすべてそれをどこでも使用するでしょう。」
Tim Schaeffer、2010年

9

基本的に、コンパイラーは、あるコードセットから別のコードセットへの変換です。ソースからIR、IRから最適化されたIR、IRからアセンブリーなどです。これは、関数型言語が設計されたものとまったく同じです。純粋な関数はあるものから別のものへの単なる変化。命令型関数にはこの品質はありません。この種のコードは命令型言語で記述できますが、関数型言語はこの言語に特化しています。


6

こちらもご覧ください

F#デザインパターン

FPは「操作」で物事をグループ化しますが、OOは「タイプ」で物事をグループ化します。コンパイラー/インタープリターにとっては「操作による」の方が自然です。


3
これは、一部のプログラミング言語理論のサークルで「式の問題」と呼ばれるものに関連しています。たとえば、この質問を参照してください。ここでは、「拡張可能な型」の方法で物事を行う本当に恐ろしいHaskellコードを示します。反対に、OOP言語を「拡張可能な操作」スタイルに強制することは、訪問者パターンに動機を与える傾向があります。
CAマッキャン

6

1つの可能性は、コンパイラーが多数のコーナーケース全体を非常に注意深く処理しなければならない傾向があることです。多くの場合、コードを正しく理解するには、実装するルールに対応する方法で実装を構造化するデザインパターンを使用する方が簡単です。多くの場合、これは命令型(シーケンス、「いつ」)の設計ではなく、宣言型(パターンマッチング、「どこ」)になるため、宣言型言語で実装するのが簡単です(そのほとんどは機能的です)。


4

誰もが別の重要な理由を見逃したようです。通常のコードの(E)BNFによく似たパーサー用の組み込みドメイン固有言語(EDSL)を書くのは非常に簡単です。Parsecのようなパーサーコンビネーターは、高階関数と関数構成を使用して、関数型言語で非常に簡単に記述できます。簡単なだけでなく、非常にエレガントです。

基本的に、最も単純な汎用パーサーを単なる関数として表し、これらのプリミティブパーサーを文法用のより複雑でより具体的なパーサーに構成できるようにする特別な操作(通常は高次関数)を使用します。

これはコースの枠組みをパーラーする唯一の方法ではありません。

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