私はオブジェクト指向のバックグラウンドを持っています。そこで、クラスは、少なくともオブジェクトの作成に使用したり、継承で使用したりできるコードの簡単なリサイクルを可能にする抽象化レイヤーを作成するために使用できることを学びました。
たとえば、私は動物のクラスを持つことができ、それから猫と犬を継承し、すべてが同じ特性の多くを継承し、それらのサブクラスから、動物の品種または名前さえ指定できるオブジェクトを作成できますそれの。
または、クラスを使用して、同じコードの複数のインスタンスを指定することもできます。検索ツリーのノードや複数の異なるデータベース接続のようなものとそうでないもの。
私は最近関数型プログラミングに移行しているので、不思議に思っていました:
純粋な関数型言語はそのようなことをどのように処理しますか?つまり、クラスやオブジェクトの概念がない言語です。
多くの関数型言語にはモジュールシステムがあります。 (ちなみに、多くのオブジェクト指向言語もそうです。)しかし、それがなくても、関数をモジュールとして使用できます。
JavaScriptが良い例です。 JavaScriptでは、関数は、モジュールの実装とオブジェクト指向のカプセル化の両方に使用されます。 JavaScriptの主要なインスピレーションとなったSchemeには、only関数があります。関数はほとんどすべてを実装するために使用されます:オブジェクト、モジュール(ラケットではnitsと呼ばれます)、さらにはデータ構造です。
OTOH、Haskell、およびMLファミリには、明示的なモジュールシステムがあります。
オブジェクト指向はデータの抽象化についてです。それでおしまい。モジュール性、継承、ポリモーフィズム、変更可能な状態でさえ、直交する関心事です。
「関数型プログラミング」はモジュール性の問題に大きな影響を与えることはありませんが、特定の言語はさまざまな方法で大規模プログラミングに対応しています。コードの再利用と抽象化は、公開する量が少ないほどコードの再利用が難しくなるという点で相互作用します。抽象化はさておき、再利用性の2つの問題に取り組みます。
静的に型付けされたOOP言語は、伝統的に名目上のサブタイピングを使用していました。つまり、クラス/モジュール/インターフェイスA向けに設計されたコードは、クラス/モジュール/インターフェイスBのみを処理できます。構造サブタイプを使用します。つまり、BがAのすべてのメソッドやフィールドを持つ場合、A向けに設計されたコードはBを処理できます。Bは、より一般的なクラス/インターフェイスが必要になる前に別のチームによって作成された可能性があります。 OCamlの例では、構造サブタイピングはモジュールシステム、OOPのようなオブジェクトシステム、およびその非常にユニークな多態バリアントタイプに適用されます。
OOPとFP wrtの最も顕著な違い。モジュール性とは、OOPのデフォルトの「ユニット」が、値として同じケースでさまざまな操作としてオブジェクトとしてバンドルされるのに対し、FPのデフォルトの「ユニット」は、関数としてバンドルされることです。値のさまざまなケースで同じ操作。 FPでは、モジュールなどとして操作をまとめることは依然として非常に簡単です。 (ところで、HaskellもF#も本格的なMLファミリモジュールシステムを持っていません。)Expression Problemは、両方の新しい操作を段階的に追加するタスクですすべての値(既存のオブジェクトに新しいメソッドをアタッチするなど)、およびすべての操作でサポートする必要のある値の新しいケース(同じインターフェイスで新しいクラスを追加するなど)。以下の最初のRalf Laemmel講義(C#に広範な例があります)で説明されているように、OOP言語では新しい操作の追加に問題があります。
OOPでのFPとScalaの組み合わせは、これが最も強力な言語の1つになる可能性があります。モジュール性。しかし、OCamlは今でも私のお気に入りの言語であり、私の個人的な主観的な意見では、Scalaを下回ることはありません。以下の2つのRalf Laemmel講義では、Haskellにおける表現の問題の解決策について説明します。このソリューションは、完全に機能しているものの、結果として得られたデータをパラメトリック多態で使用することを困難にしていると思います。以下にリンクされているJaques Garrigueの記事で説明されているOCamlの多形バリアントによる式の問題の解決には、この欠点はありません。 OCamlでの非OOPとOOPのモジュール性の使用法を比較する教科書の章にもリンクしています。
以下は、Expression Problemを拡張するHaskellおよびOCaml固有のリンクです。
「関数型言語でモジュール性を実現するにはどうすればよいですか」という2つの質問をしているようです。他の回答や「関数型言語で抽象化を作成するにはどうすればよいですか」で扱われているのはどれですか。私が答えます。
OO言語では、名詞、「動物」、「メールサーバー」、「彼の庭のフォーク」などに集中する傾向があります。対照的に、関数型言語は動詞を強調します」 「歩く」、「メールを取得する」、「本番に」など.
ですから、関数型言語の抽象化が物事よりも動詞や操作よりも多い傾向があるのは当然のことです。これを説明しようとしているときに常に到達する1つの例は、構文解析です。関数型言語では、パーサーを書く良い方法は、文法を指定してそれを解釈することです。インタープリターは、構文解析のプロセスを抽象化します。
これのもう1つの具体的な例は、私が少し前に取り組んだプロジェクトです。私はHaskellでデータベースを書いていました。最下位レベルで操作を指定するための「埋め込み言語」が1つありました。たとえば、それは私が記憶媒体から物事を読み書きすることを可能にしました。最高レベルの操作を指定するための別の「組み込み言語」がありました。次に、基本的にはインタープリターである操作を、操作を上位レベルから下位レベルに変換するために使用しました。
これは非常に一般的な抽象化形式ですが、関数型言語で使用できるのはこれだけではありません。
モジュール性は、物事を分解して元に戻すことができるように設計されており、古いものを新しいものに置き換えることもあります。
OOPはステートフルオブジェクトを定義するため、モジュール化には、集約(複数のオブジェクトから1つのオブジェクトを構築する)や調整(メッセージパッシングによるコラボレーションを可能にする)を含めることができます。これには、何らかの形の配線が必要になります(たとえば、コンストラクター依存関係の注入)。
FPは計算に焦点を当てています。ステートフルオブジェクトはまだどこかに存在している必要があります。そうしないと、何も実行されません。ただし、モジュール性は、計算(純粋な関数)の定義と組み立ての方法で管理されます。
Eric Normandは、プログラムを アクション、計算、およびデータ で構成されていると巧妙に説明しています。したがって、モジュール性には、データとアクションの分解と合成(不純なOOP Land)またはデータと計算(純粋なFP Land))が含まれます。