代数データ型で簡単に解決できる問題がいくつかあります。たとえば、リスト型は次のように簡潔に表現できます。
data ConsList a = Empty | ConsCell a (ConsList a)
consmap f Empty = Empty
consmap f (ConsCell a b) = ConsCell (f a) (consmap f b)
l = ConsCell 1 (ConsCell 2 (ConsCell 3 Empty))
consmap (+1) l
この特定の例はHaskellにありますが、代数データ型をネイティブにサポートする他の言語でも同様です。
OOスタイルのサブタイプへの明らかなマッピングがあることがわかります。データ型は抽象基本クラスになり、すべてのデータコンストラクターは具体的なサブクラスになります。Scalaの例を次に示します。
sealed abstract class ConsList[+T] {
def map[U](f: T => U): ConsList[U]
}
object Empty extends ConsList[Nothing] {
override def map[U](f: Nothing => U) = this
}
final class ConsCell[T](first: T, rest: ConsList[T]) extends ConsList[T] {
override def map[U](f: T => U) = new ConsCell(f(first), rest.map(f))
}
val l = (new ConsCell(1, new ConsCell(2, new ConsCell(3, Empty)))
l.map(1+)
単純なサブクラス化以外に必要なのは、クラスを封印する方法、つまり、サブクラスを階層に追加できないようにする方法です。
C#やJavaなどの言語でこの問題にどのように対処しますか?C#で代数データ型を使用しようとしたときに見つけた2つの障害は次のとおりです。
- C#でbottom型が何と呼ばれているのかわかりませんでした(つまり、何を入れるのかわかりませんでした
class Empty : ConsList< ??? >
) - サブクラスを階層に追加できないようにシール する方法がわかりませんでした
ConsList
C#やJavaで代数データ型を実装する最も慣用的な方法は何でしょうか?または、それが不可能な場合、慣用的な置き換えは何でしょうか?