差別的結合が関数型プログラミングに関連付けられているのはなぜですか?


30

OOプログラミングの長年にわたって、差別化されたユニオンとは何かを理解していましたが、実際に見逃したことはありませんでした。私は最近、C#で関数型プログラミングをいくつか行ってきましたが、今はそれがあればいいのにと思っています。これは私を困惑させます。なぜなら、一見すると、差別化された結合の概念は、機能的/オブジェクト指向の二分法とはまったく独立しているように見えるからです。

差別化された共用体をオブジェクト指向よりも便利にする関数型プログラミングに固有の何かがありますか、それとも「より良い」方法で問題を分析することを強制することで、単に標準を引き上げて、より良いものを要求していますか?モデル?


式の問題en.wikipedia.org/wiki/Expression_problemが関連する可能性があります
-xji

質問に対する適切な回答ではないため、代わりにコメントとして返信しますが、Ceylonプログラミング言語にはユニオン型があり、他のオブジェクト指向/混合パラダイム言語に忍び込んでいるようです。TypeScriptとScalaが思い浮かびます。また、Java言語の列挙は、識別された共用体の一種の実装として使用できます。
ローランドテップ

回答:


45

差別化された共用体は、パターンマッチングと連携して本当に輝いており、ケースに応じて異なる動作を選択します。ただし、このパターンは基本的に純粋なオブジェクト指向の原則とは相反します。

純粋なオブジェクト指向では、動作の違いはタイプ(オブジェクト)自体によって定義され、カプセル化される必要があります。したがって、パターンマッチングと等価なのは、オブジェクト自体で単一のメソッドを呼び出すことです。このメソッドは、問題のサブタイプによってオーバーロードされ、異なる動作を定義します。外部からオブジェクトのタイプを検査する(これはパターンマッチングが行うことです)アンチパターンと見なされます。

基本的な違いは、データと動作が関数型プログラミングで分離されているのに対し、データと動作はオブジェクト指向で一緒にカプセル化されていることです。

これが歴史的な理由です。C#のような言語は、より多くの機能機能を組み込むことにより、従来のOO言語からマルチパラダイム言語へと発展しています。


6
識別された共用体/和型は、継承ツリーやインターフェースを実装する複数のクラスのようなものではありません。多くの種類の値を持つ1つのタイプです。また、カプセル化を防止しません。クライアントは、あなたが複数の種類の値を持っていることを知る必要はありません。空のノード、または値と別のノードへの参照のいずれかであるリンクリストを検討してください。合計タイプがなければ、値と参照の変数を使用することで、とにかく差別化された共用体をハッキングしますが、null参照を持つオブジェクトを空のノードとして扱い、値変数が存在するふりをすることはありません。
ドーバル

2
一般に、1つのクラスに可能なすべての種類の値の変数、さらにそのインスタンスがどのような種類の値であるかを示す何らかの種類のフラグまたは列挙型を含め、それに対応しない変数の周りに踊ります種類。
ドーバル

7
@Dovalインターフェース/抽象基底クラスに型全体を表現させ、型の各ケースにサブクラスを持たせることにより、代数データ型をクラスに「標準」でエンコードします。パターンマッチングを処理するために、各ケースの関数を取るメソッドがあります。したがって、トップレベルインターフェイスにあるリストの場合List<A>、method B Match<B>(B nil, Func<A,List<A>,B> cons)です。たとえば、これはSmalltalkがブール値に使用するパターンとまったく同じです。これは基本的にScalaでの処理方法でもあります。複数のクラスが使用されるということは、公開する必要のない実装の詳細です。
デレクエルキンズ

3
@Doval:null参照を明示的にチェックすることも、実際の慣用的なオブジェクト指向とは見なされません。
ジャックB

@JacquesB nullチェックは実装の詳細です。リンクリストのクライアントには、通常isEmpty、次のノードへの参照がnullかどうかをチェックするメソッドがあります。
ドーバル

35

関数型プログラミングを学ぶ前にPascalとAdaでプログラミングしたことがあるので、差別化された共用体を関数型プログラミングに関連付けません。

差別された組合は、何らかの形で相続の二重です。最初の方法では、固定されたタイプのセット(ユニオン内のタイプ)に操作を簡単に追加でき、継承では、固定されたタイプの操作でタイプを簡単に追加できます。(両方を簡単に追加する方法は式の問題と呼ばれます;それは静的型システムを持つ言語にとって特に難しい問題です。)

オブジェクト指向は型を重視し、関数型プログラミングは関数を重視しているため、関数型プログラミング言語はユニオン型に自然に親和性があり、構文構造を使用して使いやすくしています。


7

OOでよく使用される命令型プログラミング手法は、多くの場合2つのパターンに依存しています。

  1. 例外の成功またはスロー、
  2. null「値なし」または失敗を示すために戻ります。

機能的パラダイムは通常、これらの両方を回避し、成功/失敗の理由または値/値なしを示す複合型を返すことを好みます。

差別化された組合は、これらの複合型の法案に適合します。たとえば、最初のインスタンスでtrueは、失敗を説明する何らかのデータ構造を返す場合があります。後者の場合、値が含まれている、または共用体ではnonenilなどの第二の場合、多くの関数型言語は、ビルトイン、その値/なし組合を表すために「多分」または「オプション」タイプを持っていることを、とても一般的です。

C#などで機能的なスタイルに切り替えると、これらの複合型がすぐに必要になります。void/throwそしてnull、そのようなコードに違和感を覚えます。そして、差別化された労働組合(DU)は、法案によく適合しています。したがって、多くの人がそうであるように、あなたはそれらを望んでいることに気づきました。

良いニュースは、C#などでDUをモデル化するライブラリがたくさんあることです(たとえば、自分のSuccinc <T>ライブラリを見てください)。


2

主流のオブジェクト指向言語では、オブジェクト指向サブタイプと同様のタイプの問題を解決しているため、一般に合計タイプはあまり有用ではありません。それらを見る1つの方法は、両方ともサブタイプを処理することですが、OOはopen親タイプに任意のサブタイプを追加できること、closedつまり合計タイプは有効なサブタイプを事前に決定することです。

現在、多くのオブジェクト指向言語は、サブタイピングを、継承された構造体、ポリモーフィズム、参照タイピングなどの他の概念と組み合わせて、一般的にそれらをより便利にします。その結果、(クラスやコンストラクターなどを使用して)セットアップするのに手間がかかる傾向があるため、ジェネリックタイピングが一般的になるまでsやResultsなどOptionのようなものには使用されない傾向がありました。

また、ほとんどの人がオブジェクト指向プログラミングを始めたときに学んだ実世界の関係に焦点を当てているのは、たとえば、犬is動物、整数isa結果またはエラーisa結果は少し異様に見えることを意味します。アイデアは非常に似ていますが。

関数型言語がオープンタイピングよりもクローズドタイピングを好む理由について、考えられる理由の1つは、パターンマッチングを好む傾向があることです。これは関数の多相性に役立ちますが、コンパイラがすべてのサブタイプを網羅していることをコンパイラが静的にチェックできるため、閉じた型でも非常にうまく機能します。これにより、言語に一貫性を持たせることができますが、固有の利点はないと思います(誤解される可能性があります)。


ところで、Scalaは継承を閉じています。sealedは、「同じコンパイル単位でのみ拡張できる」ことを意味します。これにより、設計時にサブクラスのセットを閉じることができます。
ヨルグWミットタグ

-3

Swiftは、 "enums"と呼ばれることを除いて、差別化された共用体を喜んで使用します。列挙型は、Swiftのオブジェクトの5つの基本的なカテゴリの1つであり、クラス、構造体、列挙型、タプル、およびクロージャです。Swiftオプションは列挙型であり、差別化された共用体であり、Swiftコードには絶対に不可欠です。

したがって、「差別的結合は関数型プログラミングに関連付けられている」という前提は間違っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.