Існують деякі проблеми, які легко вирішуються алгебраїчними типами даних, наприклад, тип списку може бути дуже коротко виражений як:
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 #, були:
- Я не міг розібратися, як називається нижній тип у C # (тобто я не міг розібратися, що вкласти
class Empty : ConsList< ??? >
) - Я не міг знайти спосіб закріплення,
ConsList
щоб ніякі підкласи не могли бути додані до ієрархії
Який був би найбільш ідіоматичний спосіб впровадження алгебраїчних типів даних у C # та / або Java? Або, якщо це неможливо, що було б ідіоматичною заміною?