関数型プログラミングには、さまざまな手法が含まれます。いくつかのテクニックは副作用で問題ありません。しかし、1つの重要な側面は方程式の推論です。同じ値で関数を呼び出しても、常に同じ結果が得られます。そのため、関数呼び出しを戻り値に置き換えて、同等の動作を得ることができます。これにより、特にデバッグの際に、プログラムについての推論が容易になります。
関数に副作用がある場合、これは完全には成立しません。戻り値には副作用が含まれていないため、戻り値は関数呼び出しと同等ではありません。
溶液は、使用を中止することにある側面の効果を、これらの影響をコード戻り値に。言語によってエフェクトシステムは異なります。たとえばHaskellはモナドを使用して、IOやState変異などの特定の効果をエンコードします。C / C ++ / Rust言語には、一部の値の変更を許可しない型システムがあります。
命令型言語では、print("foo")
関数は何かを出力して何も返しません。Haskellのような純粋な関数型言語では、print
関数は外界の状態を表すオブジェクトも取り、この出力を実行した後の状態を表す新しいオブジェクトを返します。のようなものnewState = print "foo" oldState
。古い状態から好きなだけ新しい状態を作成できます。ただし、メイン関数で使用されるのは1つだけです。そのため、関数を連鎖させることにより、複数のアクションから状態を順序付ける必要があります。印刷するためにfoo bar
、私は次のようなものを言うかもしれませんprint "bar" (print "foo" originalState)
。
出力状態が使用されない場合、Haskellは遅延言語であるため、その状態に至るまでのアクションを実行しません。逆に、この遅延はすべての効果が戻り値として明示的にエンコードされているためにのみ可能です。
Haskellは、このルートを使用する唯一の一般的に使用される関数型言語であることに注意してください。その他の関数型言語 Lispファミリ、MLファミリ、およびScalaなどの新しい関数型言語は推奨しませんが、副作用を許容します。これらは命令型関数型言語と呼ばれる可能性があります。
I / Oに副作用を使用することはおそらく問題ありません。多くの場合、I / O(ロギング以外)はシステムの外側の境界でのみ行われます。ビジネスロジック内で外部通信は発生しません。そうすれば、純粋なスタイルでソフトウェアのコアを記述しながら、外部シェルで不純なI / Oを実行できます。これは、コアがステートレスになる可能性があることも意味します。
ステートレスには、合理性とスケーラビリティの向上など、多くの実用的な利点があります。これは、Webアプリケーションのバックエンドで非常に人気があります。状態はすべて、共有データベース内の外部に保持されます。これにより、負荷分散が容易になります。セッションを特定のサーバーに固定する必要はありません。さらにサーバーが必要な場合はどうなりますか?同じデータベースを使用しているため、もう1つ追加してください。1つのサーバーがクラッシュした場合はどうなりますか?別のサーバーで保留中の要求をやり直すことができます。もちろん、データベースにはまだ状態があります。しかし、それを明示的にして抽出したので、必要に応じて、純粋な機能的アプローチを内部で使用することもできます。