3

c sum(a, b)引数の合計を返すfunction 、関数型プログラミング スタイルを定義するとしましょう。ここまでは順調ですね; FP のすべての優れた機能を問題なく使用できます。

ここで、動的型付けとシングルトンのステートフル エラー ストリームを使用する環境でこれを実行するとします。次に、処理するように設計されていない (つまり、数値ではない) aand/orの値を渡し、何らかの方法でエラーを示す必要があるとします。bsum

しかし、どのように?この関数は、純粋で副作用がないはずです。それに違反することなく、グローバルエラーストリームにエラーを挿入するにはどうすればよいですか?

4

1 に答える 1

9

私が知っているプログラミング言語には、「シングルトン ステートフル エラー ストリーム」のようなものが組み込まれていないので、それを作成する必要があります。また、プログラムを純粋な関数型スタイルで作成しようとしている場合、そのようなことはできません。

ただし、合計またはエラーの表示のいずれかを返す sum 関数を使用することはできます。これを行うために使用される型は、実際には という名前でよく知られていますEither。次に、エラーを返す可能性のある一連の計算を呼び出し、他の計算で発生したすべてのエラーのリストを返す関数を簡単に作成できます。それはあなたが話していたことにかなり近いです。グローバルではなく、明示的に返されるだけです。

関数型プログラムを作成するときの質問は、「自分が望む動作をするプログラムをどのように作成すればよいか」ということです。「別のプログラミングスタイルで採用されている特定のアプローチをどのように複製しますか?」ではありません。「グローバル ステートフル エラー ストリーム」は目的でなく手段です。純粋な関数スタイルでグローバル ステートフル エラー ストリームを使用することはできません。しかし、グローバル ステートフル エラー ストリームを使用して何を達成しているのかを自問してください。それが何であれ、同じメカニズムではなく、関数型プログラミングでそれを実現できます。

純粋な関数型プログラミングが副作用に依存する特定の手法を実装できるかどうかを尋ねることは、オブジェクト指向プログラミングでアセンブリからの手法をどのように使用するかを尋ねるようなものです。OO は、問題を解決するために使用できるさまざまなツールを提供します。これらのツールを使用して別のツールセットをエミュレートすることに制限することは、それらを使用する効果的な方法にはなりません。


コメントへの対応:エラー ストリームで達成したいことがエラー メッセージを端末に記録することである場合、はい、あるレベルで、コードはそれを行うために IO を実行する必要があります。1

端末への出力は、他の IO とまったく同じです。状態が特に避けられないように見える場合として、特に特別なことは何もありません。したがって、これがあなたの質問を「純粋な関数型プログラムは IO をどのように処理しますか?」に変えた場合、その問題について正確に語っている多くのブログ投稿やチュートリアルは言うまでもなく、SO に関する多くの重複した質問があることは間違いありません。純粋なプログラミング言語の実装者とユーザーにとって突然の驚きではありません。この質問は何十年も前からあり、回答には非常に洗練された考えが含まれています。

さまざまな言語で採用されているさまざまなアプローチがあります ( IOHaskell のモナド、Mercury のユニークなモード、Haskell の歴史的なバージョンのリクエストとレスポンスの遅延ストリームなど)。基本的な考え方は、純粋なコードで操作できるモデルを考え出し、モデルの操作を言語実装内の実際の不純な操作に接続することです。これにより、純粋性の利点を維持できます (純粋なコードには適用されるが、一般的な純粋でないコードには適用されない証明は、純粋な IO モデルを使用するコードにも適用されます)。

純粋なモデルは、実際の IO に関して意味のないことを実際に行うことができないように、慎重に設計する必要があります。たとえば、Mercury は、宇宙の現在の状態を追加のパラメーターとして渡しているかのようにプログラムを作成することで、IO を実行します。この純粋なモデルは、プログラムの外部にあるユニバースに依存し、影響を与える操作の動作を正確に表しますが、システム内のユニバースの状態が一度に 1 つだけ存在し、それが最初から最後までプログラム全体に渡される場合に限られます。 . そのため、いくつかの制限が設けられています

  1. ioは抽象化されているため、その型の値を構築する方法はありません。取得できる唯一の方法は、発信者から渡されることです。すべてを開始するために、言語実装によって値が述語にio渡されます。main
  2. ioに渡される値のモードは、main一意であるように宣言されます。これは、コンテナに入れたり、同じio値を複数の異なる呼び出しに渡すなど、重複する可能性のあることを行うことができないことを意味します。一意のモードにより、一意のモードも使用する述語にのみ値を代入できることが保証されます。ioまた、値を渡すとすぐに、値が「死んで」、他の場所に渡すことができなくなります。

1命令型プログラムであっても、エラー ログ システムに一連のエラー メッセージを返させてから、プログラムの最も外側の層の近くでそれらを出力する決定を実際に下すだけである場合、多くの柔軟性が得られることに注意してください。ログ呼び出しがすぐに出力を直接書き込んでいる場合、そのようなシステムで行うのがはるかに難しくなる頭のてっぺんから考えることができるいくつかのことを次に示します。

  • 投機的に計算を実行し、エラーが発生したかどうかをチェックして失敗したかどうかを確認します
  • 複数の高レベル システムを 1 つのシステムに結合し、ログにタグを追加して各システムを区別します。
  • エラーメッセージもある場合にのみ、デバッグおよび情報ログメッセージを発行します (したがって、デバッグするエラーがない場合は出力がきれいになり、エラーがある場合は詳細が豊富になります)
于 2012-10-28T11:08:26.477 に答える