I tipi di dati algebrici sono distinti in quanto possono essere costruiti da diversi tipi di "cose". Ad esempio, un albero non può contenere nulla (vuoto), una foglia o un nodo.
data Tree = Empty
| Leaf Int
| Node Tree Tree
Poiché un nodo è composto da due alberi, i tipi di dati algebrici possono essere ricorsivi.
La corrispondenza dei modelli consente di decostruire i tipi di dati algebrici in modo da mantenere la sicurezza dei tipi. Considera la seguente implementazione di profondità e il suo equivalente pseudocodice:
depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)
rispetto a:
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)
Questo ha lo svantaggio che il programmatore deve ricordare di includere Vuoto prima di Leaf in modo che non si acceda a field1 su un albero vuoto. Allo stesso modo, il caso Leaf deve essere dichiarato prima del caso Node in modo che non sia possibile accedere a field2 su Leaf. Pertanto la sicurezza dei tipi non è quindi mantenuta dalla lingua, ma impone al programmatore un carico cognitivo aggiuntivo. A proposito, sto prendendo questi esempi direttamente dalle pagine di Wikipedia.
Certo, una lingua che digita l'anatra potrebbe fare qualcosa del genere:
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
Quindi i tipi di dati algebrici potrebbero non essere strettamente migliori del loro equivalente OOP, ma forniscono un diverso set di tensioni con cui lavorare durante la costruzione di software.