Certains types de données algébriques permettent de résoudre facilement certains problèmes. Par exemple, un type de liste peut être exprimé de manière très succincte par:
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
Cet exemple particulier est en Haskell, mais il serait similaire dans d'autres langues avec une prise en charge native des types de données algébriques.
Il se trouve qu'il existe une correspondance évidente avec les sous-types de style OO: le type de données devient une classe de base abstraite et chaque constructeur de données devient une sous-classe concrète. Voici un exemple 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+)
La seule chose nécessaire au-delà des sous-classes naïves est un moyen de sceller les classes, c'est-à-dire de rendre impossible l'ajout de sous-classes à une hiérarchie.
Comment aborderiez-vous ce problème dans un langage comme C # ou Java? Les deux obstacles que j'ai trouvés en essayant d'utiliser des types de données algébriques en C # étaient les suivants:
- Je ne pouvais pas comprendre comment s'appelait le type du bas en C # (c'est-à-dire que je ne savais pas quoi mettre dans
class Empty : ConsList< ??? >
) - Je ne pouvais pas trouver un moyen de sceller
ConsList
afin qu'aucune sous-classe ne puisse être ajoutée à la hiérarchie
Quel serait le moyen le plus idiomatique d'implémenter les types de données algébriques en C # et / ou Java? Ou, si ce n'est pas possible, quel serait le remplacement idiomatique?