Existem alguns problemas que são facilmente resolvidos por tipos de dados algébricos, por exemplo, um tipo de lista pode ser expresso de maneira muito 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 exemplo específico está em Haskell, mas seria semelhante em outros idiomas com suporte nativo para Tipos de Dados Algébricos.
Acontece que existe um mapeamento óbvio para a subtipo no estilo OO: o tipo de dados se torna uma classe base abstrata e todo construtor de dados se torna uma subclasse concreta. Aqui está um exemplo no 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+)
A única coisa necessária além da subclasse ingênua é uma maneira de selar classes, ou seja, uma maneira de tornar impossível adicionar subclasses a uma hierarquia.
Como você abordaria esse problema em uma linguagem como C # ou Java? Os dois obstáculos que encontrei ao tentar usar tipos de dados algébricos em c # foram:
- Não consegui descobrir como é chamado o tipo inferior em C # (ou seja, não consegui descobrir em que colocar
class Empty : ConsList< ??? >
) - Não consegui descobrir uma maneira de selar
ConsList
para que nenhuma subclasse possa ser adicionada à hierarquia
Qual seria a maneira mais idiomática de implementar tipos de dados algébricos em C # e / ou Java? Ou, se não for possível, qual seria a substituição idiomática?