Os tipos de dados algébricos são distintos, pois podem ser construídos a partir de vários tipos de "coisas". Por exemplo, uma Árvore pode conter nada (Vazio), Folha ou Nó.
data Tree = Empty
| Leaf Int
| Node Tree Tree
Como um Nó é composto por duas Árvores, os tipos de dados algébricos podem ser recursivos.
A correspondência de padrões permite que os tipos de dados algébricos sejam desconstruídos de maneira a manter a segurança dos tipos. Considere a seguinte implementação de profundidade e seu equivalente em pseudocódigo:
depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)
comparado com:
switch on (data.constructor)
case Empty:
return 0
case Leaf:
return 1
case Node:
let l = data.field1
let r = data.field2
return 1 + max (depth l) (depth r)
Isso tem a desvantagem de que o programador deve se lembrar de colocar Vazio antes de Folha para que o campo1 não seja acessado em uma árvore Vazia. Da mesma forma, o caso Leaf deve ser declarado antes do caso Node para que o campo2 não seja acessado no Leaf. Portanto, a segurança do tipo não é mantida pela linguagem, mas impõe carga cognitiva adicional ao programador. A propósito, eu estou pegando esses exemplos diretamente das páginas da Wikipedia.
Obviamente, um idioma de digitação de pato poderia fazer algo assim:
class Empty
def depth
0
end
end
class Leaf
def depth
1
end
end
class Node
attr_accessor :field1, :field2
def depth
1 + [field1.depth, field2.depth].max
end
end
Portanto, os tipos de dados algébricos podem não ser estritamente melhores que os equivalentes a OOP, mas fornecem um conjunto diferente de tensões para trabalhar na construção de software.