Hay algunos problemas que los tipos de datos algebraicos pueden resolver fácilmente, por ejemplo, un tipo de lista puede expresarse de manera muy sucinta como:
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
Este ejemplo particular está en Haskell, pero sería similar en otros idiomas con soporte nativo para tipos de datos algebraicos.
Resulta que hay un mapeo obvio para el subtipo de estilo OO: el tipo de datos se convierte en una clase base abstracta y cada constructor de datos se convierte en una subclase concreta. Aquí hay un ejemplo en 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+)
Lo único que se necesita más allá de las subclases ingenuas es una forma de sellar clases, es decir, una forma de hacer que sea imposible agregar subclases a una jerarquía.
¿Cómo abordarías este problema en un lenguaje como C # o Java? Los dos escollos que encontré al intentar usar tipos de datos algebraicos en C # fueron:
- No pude averiguar cómo se llama el tipo de fondo en C # (es decir, no pude averiguar en qué poner
class Empty : ConsList< ??? >
) - No pude encontrar una manera de sellar
ConsList
para que no se puedan agregar subclases a la jerarquía
¿Cuál sería la forma más idiomática de implementar tipos de datos algebraicos en C # y / o Java? O, si no es posible, ¿cuál sería el reemplazo idiomático?