11

私は関数型言語の核となる概念に頭を悩ませようとしています:

「関数型言語の中心的な概念は、関数の結果がその入力によって決定され、その入力のみによって決定されるということです。副作用はありません!」

http://www.haskell.org/haskellwiki/Why_Haskell_matters#Functions_and_side-effects_in_functional_languages

私の質問は、関数がそのローカル環境内でのみ変更を行い、結果を返す場合、データベースまたはファイル システムとどのようにやり取りできるかということです。定義上、それは実質的にグローバル変数またはグローバル状態にアクセスすることではないでしょうか?

これを回避または対処するために使用される最も一般的なパターンは何ですか?

4

3 に答える 3

12

関数型言語が機能的であるという理由だけで(Haskellのように完全に純粋でさえあるかもしれません!)、その言語で書かれたプログラムが実行されたときに純粋でなければならないという意味ではありません。

たとえば、副作用を処理する場合のHaskellのアプローチは、かなり簡単に説明できます。プログラム全体を純粋にします(つまり、関数は同じ引数に対して常に同じ値を返し、副作用はありません)。関数の戻り値をmain実行可能なアクションとします。

これを擬似コードで説明しようとすると、命令型非関数型言語のプログラムがあります。

main:
  read contents of abc.txt into mystring
  write contents of mystring to def.txt

上記のmain手順はまさにそれです:一連のアクションを実行する方法を説明する一連のステップ。

これをHaskellのような純粋に関数型の言語と比較してください。関数型言語では、main関数を含め、すべてが式です。したがって、次のように上記のプログラムに相当するものを読み取ることができます。

main = the reading of abc.txt into mystring followed by
       the writing of mystring to def.txt

つまり、mainは、評価されると、プログラムを実行するために何をすべきかを説明するアクションを返す式です。このアクションの実際の実行は、プログラマーの世界の外で行われます。そして、これは実際にそれがどのように機能するかです。以下は、コンパイルして実行できる実際のHaskellプログラムです。

main = readFile "abc.txt" >>= \ mystring ->
       writeFile "def.txt" mystring

a >>= bこの状況では、「行動aに続いて行動にa与えられた結果」を意味すると言うことができ、オペレーターの結果は、行動aとbを組み合わせたものです。b上記のプログラムはもちろん慣用的なHaskellではありません。次のように書き直すことができます(余分な変数を削除します)。

main = readFile "abc.txt" >>=
       writeFile "def.txt"

...または、構文糖衣と表記法を使用します。

main = do
  mystring <- readFile "abc.txt"
  writeFile "def.txt" mystring

上記のプログラムはすべて同等であるだけでなく、コンパイラーに関する限り同一です。

これは、ファイル、データベースシステム、およびWebサーバーを純粋に機能的なプログラムとして作成する方法です。プログラムにアクション値をスレッド化して結合し、最終的にmain関数に変換します。これにより、プログラマーはプログラムを非常に細かく制御できます。そのため、純粋に関数型プログラミング言語が状況によっては非常に魅力的です。

于 2011-12-06T21:32:22.110 に答える
6

関数型言語で副作用と不純物を処理するための最も一般的なパターンは次のとおりです。

  • 純粋主義者ではなく、実用的であること
  • 不純なコードと副作用を許容するビルトインを提供する
  • それらをできるだけ使用しないでください!

例:

  • Lisp/スキーム: set!
  • Clojure: refs、および Java オブジェクトでの変更メソッドの使用
  • Scala:変数の作成var
  • ML: 詳細は不明ですが、ウィキペディアによると、多少の不純物は許容されます

Haskell は少しごまかしています。その解決策は、ファイル システムまたはデータベースにアクセスする関数の場合、 filesystem/db の状態を含む、その時点でのユニバース全体の状態が関数に渡されることです。 (1) したがって、その瞬間の宇宙全体の状態を再現できれば、そのような関数から同じ結果を 2 回得ることができます。もちろん、その瞬間の宇宙全体の状態を複製することはできないため、関数は異なる値を返します...

しかし、Haskell のソリューションである IMHO は、最も一般的なものではありません。


(1)詳細は不明。この比喩は使い古されており、おそらくそれほど正確ではないことを指摘してくれた CAMcCann に感謝します。

于 2011-12-06T22:00:25.130 に答える
4

データベースへのアクセスは、 などの他の入出力の場合と同じprint(17)です。

LISP や ML のような熱心に評価されている言語では、効果的なプログラミングへの通常のアプローチは、他のほとんどのプログラミング言語と同様に、副作用を使用することです。

Haskell では、IO 問題の解決策はモナドを使用することです。たとえば、Haskell データベース ライブラリであるHDBCを確認すると、多くの関数が IO アクションを返すことがわかります。

Clean などの一部の言語では、ユニーク型を使用して、Haskell がモナドに対して行うのと同じ種類の連続性を強制しますが、これらの言語は最近では見つけるのが難しくなっています。

于 2011-12-06T22:14:07.507 に答える