Esistono alcuni problemi che possono essere facilmente risolti dai tipi di dati algebrici, ad esempio un tipo di elenco può essere espresso in modo molto sintetico come:
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
Questo esempio particolare è in Haskell, ma sarebbe simile in altre lingue con il supporto nativo per i tipi di dati algebrici.
Si scopre che esiste una mappatura ovvia al sottotitolo in stile OO: il tipo di dati diventa una classe base astratta e ogni costruttore di dati diventa una sottoclasse concreta. Ecco un esempio in 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+)
L'unica cosa necessaria oltre l'ingenua sottoclasse è un modo per sigillare le classi, cioè un modo per rendere impossibile aggiungere sottoclassi a una gerarchia.
Come affronteresti questo problema in un linguaggio come C # o Java? I due blocchi che ho trovato durante il tentativo di utilizzare i tipi di dati algebrici in C # erano:
- Non sono riuscito a capire come si chiama il tipo di fondo in C # (cioè non sono riuscito a capire in cosa inserire
class Empty : ConsList< ??? >
) - Non sono riuscito a trovare un modo per sigillare in
ConsList
modo che nessuna sottoclasse possa essere aggiunta alla gerarchia
Quale sarebbe il modo più idiomatico per implementare i tipi di dati algebrici in C # e / o Java? Oppure, se non fosse possibile, quale sarebbe la sostituzione idiomatica?