Есть некоторые проблемы, которые легко решаются алгебраическими типами данных, например, тип List может быть очень кратко выражен как:
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, но в других языках он будет аналогичным с нативной поддержкой алгебраических типов данных.
Оказывается, существует очевидное сопоставление с подтипами в стиле ОО: тип данных становится абстрактным базовым классом, а каждый конструктор данных становится конкретным подклассом. Вот пример в 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 #:
- Я не мог понять, как называется нижний тип в C # (то есть я не мог понять, что поместить в
class Empty : ConsList< ??? >
) - Я не мог найти способ запечатать,
ConsList
чтобы никакие подклассы не могли быть добавлены к иерархии
Каков был бы самый идиоматический способ реализации алгебраических типов данных в C # и / или Java? Или, если это невозможно, какой будет идиоматическая замена?