Istnieją pewne problemy, które można łatwo rozwiązać za pomocą Algebraicznych typów danych, na przykład typ listy można bardzo zwięźle wyrazić jako:
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
Ten konkretny przykład jest w języku Haskell, ale byłby podobny w innych językach z natywną obsługą algebraicznych typów danych.
Okazuje się, że istnieje oczywiste odwzorowanie na podtypy w stylu OO: typ danych staje się abstrakcyjną klasą bazową, a każdy konstruktor danych staje się konkretną podklasą. Oto przykład w Scali:
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+)
Jedyną rzeczą potrzebną poza naiwną podklasą jest sposób uszczelniania klas, tj. Sposób uniemożliwiający dodanie podklas do hierarchii.
Jak podchodziłbyś do tego problemu w języku takim jak C # lub Java? Dwa potknięcia, które znalazłem podczas próby użycia Algebraicznych Typów Danych w C # to:
- Nie mogłem dowiedzieć się, jak nazywa się typ dna w C # (tzn. Nie mogłem dowiedzieć się, w co należy włożyć
class Empty : ConsList< ??? >
) - Nie mogłem wymyślić sposobu uszczelnienia
ConsList
, aby nie można było dodawać żadnych podklas do hierarchii
Jaki byłby najbardziej idiomatyczny sposób implementacji Algebraicznych typów danych w C # i / lub Javie? Lub, jeśli nie jest to możliwe, jaki byłby idiomatyczny zamiennik?