STDOUTとその不純


10

私は関数型プログラミングに関する多くの本や記事を読みましたが、いくつかの非常に基本的な概念を確実に理解できなかったことを恥ずかしく思っています。

関数型プログラミングの主なアイデアの1つは、同じ入力は常に同じ出力を生成するということです。したがって、データベースのクエリやファイルの書き込みは、純粋に機能的なスタイルで行うことはできません。たとえば、モナドが必要な理由の1つです。

問題は、なぜSTDOUT出力を不純なものと見なすのかです。はい、どのファイルハンドラーも危険です-データが常に書き込まれると確信することはできません。しかし、STDOUTはどうですか?なぜそれを信頼できないものと考えるべきなのでしょうか?評価自体の方が信頼性が低いですか?つまり、トリガーを引いて、計算を中断することができます。

回答:


6

したがって、データベースのクエリやファイルの書き込みは、純粋に機能的なスタイルで行うことはできません。たとえば、モナドが必要な理由の1つです。

モナドを「必要とする」人はいません。それは物事を説明する1つの方法にすぎません。実際、それはおそらく最善の方法でもありません。何らかの効果の型付け一意性の型、または完全線形論理に基づくシステムは、理論上は説得力があるように見えますが、よく知られている型システムからの根本的な逸脱であり、表現するのがより複雑です。Haskellに見られるモナディックIOは、言語で既に使用されている既存のMLスタイルの型システムと簡単に共存できる方法で完全に命令型プログラミングを本質的にモデル化するため、使いやすさとシンプルさの妥協点です。

問題は、なぜSTDOUT出力を不純なものと見なすのかです。はい、どんなファイルハンドラーも危険です-データが常に書き込まれると確信することはできません。しかし、STDOUTはどうですか?なぜそれを信頼できないものと考えるべきなのでしょうか?評価自体の方が信頼性が低いですか?つまり、トリガーを引いて、計算を中断することができます。

そうではありません。プログラム全体への入力およびプログラム全体からの出力は、単に引数と見なすことができ、プログラム全体を1つの大きな純粋な関数として扱った結果です。stdinから同じものをフィードした場合、同じものをstdoutに出力する限り、それは純粋な関数です。実際、モナディックIOを導入する前に、Haskellは入出力に純粋な遅延ストリームを使用するストリームベースのI / Oシステムを使用していました。それは明らかに使用するのが苦痛だったのでそれを落としました、それはなぜあなたがこのような何かを聞いたことがないのかについてあなたにいくつかの考えを与えるかもしれません。:]

馬鹿げた方法で要点を述べるために、ミニマリストの難解な言語であるレイジーKを考えてみましょう。

レイジーKは、ガベージコレクションされた参照透過的な関数型プログラミング言語であり、シンプルなストリームベースのI / Oシステムを備えています。

Lazy Kを他のそのような言語と区別するのは、他の機能がほとんどないことです。たとえば、統合されたHindley-Milner多態型システムは提供されません。プラットフォームに依存しないGUIプログラミングと他の言語へのバインディングをサポートする広範な標準ライブラリは付属していません。特に、Lazy Kは組み込み以外の関数を定義または参照する方法を提供していないため、このようなライブラリを作成することもできません。この機能がないことは、数値、文字列、またはその他のデータ型のサポートが対応していないことによって補完されます。それにもかかわらず、Lazy Kはチューリング完全です。

(...)

レイジーKプログラムは、数学関数と同じ時代を超越したプラトニックの領域に住んでいます。ガベージコレクションがメモリ管理のプロセスをプログラマから隠すのと同じように、参照の透過性は評価のプロセスを隠します。マンデルブロ集合の画像を表示するために、またはレイジーKプログラムを「実行」するために、いくつかの計算が必要であるという事実は、実装の詳細です。それが関数型プログラミングの本質です。

(...)

副作用のない言語で入力と出力を処理する方法は?ある意味では、入力と出力は副作用ではありません。それらは、いわば、フロントエフェクトとバックエフェクトです。したがって、それはLazy Kにあり、プログラムは可能な入力の空間から可能な出力の空間への関数として単純に扱われます。

それよりも純粋な関数型言語が見つかるとは思えません!


ただし、上記は本質的に純粋な関数の入力と出力を受け取り、それらを何らかの方法で「外部」でstdin / stdoutに接続する場合にのみ適用されることに注意してください。それと実際のシステムレベルのI / Oプリミティブへのアクセスとの間には大きな違いがあります。ストリームへの読み書きの実装の詳細は、慎重にカプセル化しない限り、不純物をリークする可能性があります。

これがHaskellで直接これを行うことができない主な理由だと思います-賢明なユースケースはモナディックIOを使用する場合に比べてスリムであり、後者の場合、本物にアクセスできることには多くの利点があります。たとえば、プログラムへのコマンドライン引数は、main直感的にはそうであるように見えても、単純に引数としてに渡されないのはそのためだと思います。

ただし、特定のプログラムでこのような最小バージョンを回復することはできます。引数を純粋な値としてキャプチャし、プログラムの残りの部分で関数を使用するinteractだけです。


サー、私は告白しなければなりません、私はどのスタックでもあなたの答えを楽しんでいます。あなたは間違いなく本Haskellを書くべきです、そして私は冗談ではありません。
shabunc '15

@shabunc:私はときどき、SOに対する私の回答の合計が本のサイズになるのにどれほど近いか疑問に思っていました...
CA McCann

完全線形論理に基づくシステムの例を挙げていただけますか?それが存在する場合、それは興味深いようです。
コンフィギュレー

@configurator:他の言語では特定の言語にリンクしているが、線形論理ではWikipediaページにリンクしていることに注意してください。悲しいかな、もし私に例があれば、私はそれを与えたでしょう。:[私が聞いたすべては、CS研究からの部分的なプロトタイプと実験システムです。さらに詳しく知りたい場合は、線形型システムについて比較的親しみやすい資料をいくつか紹介します。
CAマッキャン2011

3

関数型プログラムの純粋さは価値のある目標ですが、現実には、あなたが言及した理由により、すべての自明ではない有用なプログラムにはいくつかの不純(または「副作用」)があります。

完全に純粋なプログラムは、定義により、封印されたブラックボックスであり、本質的に興味深いものではありません。

関数型言語Haskellは、モナドでの出力などの副作用を分離することにより、この問題を処理します。モナドは、純粋に関数型のプログラミングスタイルを維持しながら、出力を生成することを可能にします。


確かに、あなたは正しいです。しかし、100%の純粋さがユートピアである理由を理解しています。私はただSTDOUTについて質問します。
shabunc 2011

1
STDOUTは、他と同様に副作用です。内部的には、モナドは必要になるかもしれないエラーチェックを実行します。
Robert Harvey

はい、それはこの質問が何についてであるかです-他のようになぜそれが副作用であると考えられましたか?
shabunc '15

2
外の世界を変更するものはすべて副作用と見なされます。
Robert Harvey、

1

端末デバイスに接続していない場合、または(何らかの理由で)ファイル記述子を閉じた場合、STDOUTへの書き込みは失敗する可能性があります。

また、STDOUTは常に「コンソール画面」であるとは限りません。パイプで別のプログラムに渡されることもあります。時々パイプが壊れています。


0

「外の世界の状態を変える」という言葉で純粋さを考えると役立ちます。これには、コンソール、ログファイルへの書き込み、CDの取り出し、または「ミサイルの起動」が含まれます。

また、同時実行に関しても問題になる可能性があります。関数に副作用がないことがわかっている場合は、競合状態などがないことを証明できるため、同時実行性を簡単に調整できます。


外界の状態を変更する、外界の状態に依存します。これらの線に沿ってより多くの議論についてはこの質問を参照してください。
MatrixFrog 2011
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.