Ci sono alcuni problemi che sono facilmente risolvibili con i tipi di dati algebrici, ad esempio un tipo di lista può essere espresso in modo succinto 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 particolare esempio è 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 sottotipo 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 la sottoclasse ingenua è un modo per classificare seal , ovvero un modo per rendere impossibile aggiungere sottoclassi a una gerarchia.
Come affronteresti questo problema in un linguaggio come C # o Java? I due ostacoli che ho trovato durante il tentativo di utilizzare i tipi di dati algebrici in C # erano:
- Non riuscivo a capire quale sia il nome in basso chiamato in C # (cioè non riuscivo a capire cosa mettere in
class Empty : ConsList< ??? >
) - Non riuscivo a trovare un modo per sigillare
ConsList
in 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 in Java? Oppure, se non è possibile, quale sarebbe la sostituzione idiomatica?