どちらの概念でも、新しいデータ型を作成できます。私が見ることができる唯一の違いは、関数型言語では、代数的データ型でパターンマッチングを実行できることです。しかし、オブジェクト指向言語に匹敵する簡潔な機能はありません。これは正確な記述ですか?
5 に答える
代数的データ型は、「始代数」を形成するため、そのように名付けられています。
+ represents sum types (disjoint unions, e.g. Either).
• represents product types (e.g. structs or tuples)
X for the singleton type (e.g. data X a = X a)
1 for the unit type ()
and μ for the least fixed point (e.g. recursive types), usually implicit.
これらの演算子から、すべての通常のデータ型を作成できます。代数的データ型は、パラメトリックポリモフィズムもサポートします。つまり、静的な安全性が保証された、基になる型のコンテナーとして使用できます。さらに、ADTには、(コンストラクターとパターンマッチングを介して)データ型を導入および削除するための統一された構文が用意されています。例えば
-- this defines a tree
data Tree a = Empty | Node a (Tree a) (Tree a)
-- this constructs a tree
let x = Node 1 (Node 2 Empty) Empty
-- this deconstructs a tree
f (Node a l r) = a + (f l) + (f r)
代数的データ型の豊富さと均一性は、それらが不変であるという事実とともに、それらをオブジェクト指向オブジェクトと区別します。
- 製品タイプのみを表します(したがって、再帰型または合計型はありません)
- パターンマッチングをサポートしていません
- 変更可能
- パラメトリック多型をサポートしない
代数的データ型とOOスタイルのクラスの間には、(不)可変性が異なるためカウントしない、3つの大きな違いがあります。
- 代数的データ型では、合計と積が許可されますが、OOスタイルのクラスでは積のみが許可されます。
- OOスタイルのクラスでは、複雑なデータ項目を受け入れられた操作にバンドルできますが、代数的データ型ではバンドルできません。
- 代数的データ型は、コンストラクターに渡されたデータと結果の値に格納されたデータを区別しませんが、OOスタイルのクラスは区別します(または区別できます)。
そのリストから意図的に除外したことの1つは、サブタイピングでした。大多数のオブジェクト指向言語ではクラスをサブクラス化でき(非最終、非封印、現在アクセス可能)、一般的にMLファミリーの機能言語の大多数ではできませんが、仮想的な継承を完全に禁止することは明らかに可能です。オブジェクト指向(または少なくともオブジェクト指向のような)言語であり、代数データ型でサブタイピングとスーパータイピングを生成することも同様に可能です。後者の限定的な例については、Timberに引き継がれたO'Haskellのこのページを参照してください。
クラスは単なる型定義ではありません。ほとんどのオブジェクト指向言語のクラスは、実際には、あらゆる種類のゆるやかに関連する機能を提供するキッチンシンク機能です。
特に、クラスは一種のモジュールとして機能し、データの抽象化と名前空間を提供します。代数的データ型にはこれが組み込まれていません。モジュール性は通常、別個の直交機能(通常はモジュール)として提供されます。
ある意味で、このように見ることができます。すべての言語には、ユーザー定義型を作成するためのメカニズムが非常に多くあります。機能(ML、Haskellスタイル)言語では、ADTの作成だけがあります。(HaskellのニュータイプはADTの退化したケースと見なすことができます)。オブジェクト指向言語では、それはクラスです。手続き型言語ではstruct
、またはrecord
です。
言うまでもなく、ユーザー定義のデータ型のセマンティクスは言語ごとに異なり、パラダイム#1の言語からパラダイム#2の言語までさらに異なります。@Pharien's Flameは、すでに典型的な違いを概説しています。
代数的データ型の概念は、オブジェクト指向言語のクラス定義に似ていますか?
関数型言語では、代数的データ型に対してパターンマッチングを実行できます。しかし、オブジェクト指向言語に匹敵する簡潔な機能はありません。これは正確な記述ですか?
それはその一部です。
Andreasが言ったように、クラスは、C ++、Java、C#などのSimulaから派生した静的に型付けされたオブジェクト指向言語のキッチンシンク機能です。クラスはすべての取引のジャックですが、この点で機能のマスターはありません。クラスは多くの問題をうまく解決しません。
C ++、Java、C#で見られるように、バニラMLとHaskellをOOと比較すると、次のようになります。
- クラスには他のクラスを含めることができますが、代数的データ型は相互に参照できますが、相互の定義を含めることはできません。
- クラス階層は任意の深さにすることができますが、代数的データ型は1レベルの深さです。型には型コンストラクターが含まれ、それだけです。
- 新しいクラスは古いクラスから派生できるため、クラスは拡張可能な型ですが、代数的データ型は通常(常にではありませんが)閉じられます。
したがって、ADTは、1つの特定の問題、つまり1レベルの深さのクラス階層のみを解決するため、実際にはクラスに「類似」していません。この意味で、2つのおおよその観察結果を見ることができます。
- ADTには、継承よりも構成が必要です。
- クラスを使用すると、型を拡張するのは簡単ですが、メンバー関数のセットを拡張するのは困難です。一方、ADTを使用すると、型を超えて関数を拡張するのは簡単ですが、型を拡張するのは困難です。
GoFデザインパターンもご覧ください。これらは、C++のクラスを使用して表現されています。同等の機能は必ずしもADTではなく、コマンドパターンの代わりにラムダ関数のようなものや、ビジターパターンの代わりになどの高階関数などmap
ですfold
。