web-dev-qa-db-ja.com

関数型プログラミング言語は副作用を許可しませんか?

Wikipediaによると、宣言型である 関数型プログラミング言語 であり、副作用を認めていません。 宣言型プログラミング 一般に、副作用を最小限に抑えるか、排除しようとします。

また、ウィキペディアによると、副作用は状態変化に関連しています。したがって、関数型プログラミング言語は、その意味で、状態を保存しないため、実際には副作用を排除します。

しかし、それに加えて 副作用 には別の定義があります。副作用

値を返す以外に、呼び出し元の関数または外界との観測可能な相互作用があります。たとえば、特定の関数は、グローバル変数または静的変数の変更、その引数の1つの変更、例外の発生、ディスプレイまたはファイルへのデータの書き込み、データの読み取り、または他の副作用のある関数の呼び出しを行う場合があります。

その意味で、関数型プログラミング言語は実際には副作用を許容します。外部の世界に影響を与える関数の例は他にも無数にあり、他の関数の呼び出し、例外の発生、ファイルへの書き込みなどです。

それで、最後に、関数型プログラミング言語は副作用を許可するかどうか?

または、何が「副作用」とみなされるか理解していないため、命令型言語では許可されており、宣言型言語では許可されていません。上記と私が得た結果によると、副作用を排除する言語はないため、副作用について何か欠けているか、ウィキペディアの定義が誤って広範です。

10
codebot

関数型プログラミングには、さまざまな手法が含まれます。いくつかのテクニックは副作用で問題ありません。しかし、重要な側面の1つは式の推論です。同じ値で関数を呼び出しても、常に同じ結果が得られます。そのため、関数呼び出しを戻り値に置き換えて、同等の動作を得ることができます。これにより、特にデバッグの際に、プログラムについての推論が容易になります。

関数に副作用がある場合、これは完全には成立しません。戻り値には副作用が含まれていないため、戻り値は関数呼び出しと同等ではありません。

解決策は、sideエフェクトの使用とこれらのエフェクトのエンコード戻り値内を停止することです。言語によってエフェクトシステムは異なります。例えば。 Haskellはモナドを使用して、IOまたはStateミューテーションなどの特定の効果をエンコードします。C/ C++/Rust言語には、一部の値のミューテーションを許可しない型システムがあります。

命令型言語では、print("foo")関数は何かを出力し、何も返しません。 Haskellのような純粋な関数型言語では、print関数も外界の状態を表すオブジェクトを取り、この出力を実行した後の状態を表す新しいオブジェクトを返します。 _newState = print "foo" oldState_に似たもの。古い状態から好きなだけ新しい状態を作成できます。ただし、メイン関数で使用されるのは1つだけです。したがって、関数をチェーンすることにより、複数のアクションから状態を順序付ける必要があります。 _foo bar_を出力するには、print "bar" (print "foo" originalState)のように言います。

出力状態が使用されない場合、Haskellは遅延言語であるため、その状態に至るまでのアクションを実行しません。逆に、この遅延は、すべての効果が戻り値として明示的にエンコードされているためにのみ可能です。

Haskellはonlyであり、このルートを使用する一般的に使用される関数型言語であることに注意してください。その他の関数型言語LISPファミリ、MLファミリ、およびScalaなどの新しい関数型言語は推奨されませんが、副作用は許容されます。これらは命令型関数型言語と呼ばれる可能性があります。

I/Oに副作用を使用することはおそらく問題ありません。多くの場合、I/O(ロギング以外)はシステムの外側の境界でのみ行われます。ビジネスロジック内で外部通信は発生しません。その後、外部シェルで純粋でないI/Oを実行しながら、ソフトウェアのコアを純粋なスタイルで書き込むことができます。これは、コアがステートレスになる可能性があることも意味します。

ステートレスには、合理性とスケーラビリティの向上など、多くの実用的な利点があります。これは、Webアプリケーションのバックエンドで非常に人気があります。状態はすべて、共有データベース内の外部に保持されます。これにより、負荷分散が容易になります。セッションを特定のサーバーに固定する必要はありません。さらにサーバーが必要な場合はどうなりますか?同じデータベースを使用しているため、もう1つ追加してください。 1つのサーバーがクラッシュした場合はどうなりますか?別のサーバーで保留中の要求をやり直すことができます。もちろん、データベースにはまだ状態があります。しかし、それを明示的にして抽出したので、必要に応じて内部で純粋な機能的アプローチを使用できます。

26
amon

プログラミング言語は副作用を排除しません。宣言型言語には副作用が含まれているが、命令型言語には含まれていないと言った方がいいと思います。ただし、副作用についてのこの話のいずれかが2種類の言語間の根本的な違いを理解しているかどうかはよくわかりません。

例で違いを説明するのに役立つと思います。

a = b + c

上記のコード行は実質的に任意の言語で記述できるため、命令型言語と宣言型言語のどちらを使用しているかをどのように判断できますか?そのコード行のプロパティは、2つの言語クラスでどのように異なりますか?

命令型言語(C、Java、Javascriptなど)では、そのコード行はプロセスのステップを表すだけです。これは、値の基本的な性質について何も教えてくれません。このコード行の後(ただし次の行の前)に、abcに等しくなりますが、aについては大まかに何も伝えていません。

宣言型言語(Haskell、Scheme、Excelなど)では、そのコード行はさらに多くのことを示しています。これは、aabが等しい場合に常にになるように、cと他の2つのオブジェクトとの間に不変の関係を確立します。 bまたはcで値が変更されても、aがそれらの合計に等しいという事実が残るため、Excelを宣言型言語のリストに含めたことに注意してください。

私の考えではこれは副作用や状態ではなく、2種類の言語の違いです。命令型言語では、特定のコード行は、問題の変数の全体的な意味については何も伝えません。言い換えると、 a = b + cは、非常に短い間、aが偶然にbcの合計に等しいことを意味します。

一方、宣言型言語ではeveryのコード行により、プログラムの存続期間全体にわたって存在する基本的な真実が確立されます。これらの言語では、a = b + cは、他のコード行で何が発生しても、aは常にbcの合計と等しくなることを示しています。

5
Daniel T.