公正な警告、私は関数型プログラミングに不慣れなので、多くの悪い仮定を抱くかもしれません。
私は代数型について学んでいます。多くの関数型言語はそれらを持っているようであり、それらはパターンマッチングと組み合わせてかなり有用です。しかし、彼らは実際にどのような問題を解決しますか?次のように、C#で一見(一種の)代数型を実装できます。
public abstract class Option { }
public class None : Option { }
public class Some<T> : Option
{
public T Value { get; set; }
}
var result = GetSomeValue();
if(result is None)
{
}
else
{
}
しかし、私はこれがオブジェクト指向プログラミングの卑劣化であることに同意するだろうと私は思うだろう。それでは、関数型プログラミングは、このスタイルのプログラミングを粗雑に見せないようにするより明確な構文を追加するだけですか?他に何が欠けていますか?
インターフェイスと継承を持つクラスは、オープンな世界を提供します:誰でも新しい種類のデータを追加できます。特定のインターフェイスについて、世界中のさまざまなファイル、さまざまなプロジェクト、さまざまな企業でそれを実装するクラスが存在する場合があります。データ構造にケースを簡単に追加できますが、インターフェースの実装が分散されているため、インターフェースに新しいメソッドを追加することは困難です。インターフェースが公開されると、基本的にフリーズされます。可能な実装をすべて知っている人はいません。
代数的データ型はそれと二重であり、それらはclosedです。データのすべてのケースが1か所にリストされており、操作はcanだけでなく、バリアントを完全にリストしますそうするように勧められました。したがって、代数的データ型を操作する新しい関数を書くのは簡単です:いまいましい関数を書くだけです。その代わりに、基本的にコードベース全体を調べてすべてのmatch
を拡張する必要があるため、新しいケースの追加は複雑です。インターフェイスの状況と同様に、Rust標準ライブラリ)では、 新しいバリアントの追加は重大な変更です (パブリックタイプの場合)。
これらは 式の問題 の両面です。代数的データ型はそれらに対する不完全なソリューションですが、OOPもそうです。どちらも、データのケース数、それらのケースが変更される頻度、および操作が拡張または変更される頻度に応じて、利点があります。 (これが、多くの現代の言語が両方または類似のものを提供するか、両方のアプローチを包括しようとする、より強力でより複雑なメカニズムに直接進む理由です。)
それでは、関数型プログラミングは、このスタイルのプログラミングを粗雑に見せないようにするより明確な構文を追加するだけですか?
それはおそらく単純化ですが、そうです。
他に何が欠けていますか?
代数的データ型が何であるかを明確にしましょう(これをまとめると link Learn you as Haskellから):
あなたの例は本当に最初のものでのみ機能します。
おそらく不足しているのは、これらの2つの基本的な操作を提供することで、関数型言語で他のすべてを構築できることです。 C#には、その上に構造体、クラス、列挙型、ジェネリックス、およびこれらの動作を制御するためのルールの山があります。
機能するいくつかの構文と組み合わせると、関数型言語は操作をこれら2つのパスに分解できるため、型に対してクリーンでシンプルかつエレガントなアプローチを提供できます。
代数的データ型はどのような問題を解決しますか?
これらは、他の型システムと同じ問題を解決します。「ここで使用できる値は何ですか?」 -彼らはそれに異なるアプローチをとるだけです。
パターンマッチングはオプションを操作するための最も慣用的な方法とは見なされないことを知って驚くかもしれません。詳しくは Scalaのオプションのドキュメント を参照してください。なぜこんなに多くのFPチュートリアルがこの使用法を奨励するのか、私にはわかりません。
主に欠けているのは、オプションの操作を簡単にするために作成された関数がたくさんあることです。 Scala docsの主な例を考えてみましょう:
val name: Option[String] = request getParameter "name"
val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
println(upper getOrElse "")
map
があるかどうかを各ポイントで確認する必要なく、filter
とNone
を使用してオプションの操作をチェーンできることに注意してください。次に、最後にgetOrElse
を使用してデフォルト値を指定します。タイプをチェックするような「大まかな」ことをしていることはありません。不可避の型チェックはライブラリ内部で行われます。 Haskellには 独自のセット の類似した関数があり、モナドやファンクタで機能する関数の大きなセットは言うまでもありません。
他の代数的データ型には、それらを操作するための独自の慣用的な方法があり、ほとんどの場合、トーテムポールでのパターンマッチングは低くなります。同様に、独自のタイプを作成するときは、それらを操作するための同様の関数を提供することが期待されます。