Los tipos de datos algebraicos son distintos porque pueden construirse a partir de varios tipos de "cosas". Por ejemplo, un árbol puede contener nada (vacío), una hoja o un nodo.
data Tree = Empty
| Leaf Int
| Node Tree Tree
Como un nodo está compuesto por dos árboles, los tipos de datos algebraicos pueden ser recursivos.
La coincidencia de patrones permite que los tipos de datos algebraicos se deconstruyan de una manera que mantenga la seguridad de los tipos. Considere la siguiente implementación de profundidad y su equivalente de pseudocódigo:
depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)
comparado con:
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)
Esto tiene la desventaja de que el programador debe acordarse de vaciar antes que Leaf para que no se acceda al campo 1 en un árbol vacío. Del mismo modo, el caso Leaf debe declararse antes del caso Node para que no se acceda al campo2 en Leaf. Por lo tanto, la seguridad de tipo no es mantenida por el lenguaje, sino que impone una carga cognitiva adicional en el programador. Por cierto, estoy tomando estos ejemplos directamente de las páginas de wikipedia.
Por supuesto, un lenguaje de escritura de pato podría hacer algo como esto:
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
Por lo tanto, los tipos de datos algebraicos pueden no ser estrictamente mejores que su equivalente de OOP, pero proporcionan un conjunto diferente de tensiones para trabajar al construir software.