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 ai tipi di dati algebrici di essere decostruiti in modo da mantenere la sicurezza del tipo. Considera la seguente implementazione della 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 il caso Empty prima di Leaf in modo che il campo 1 non sia accessibile su un albero vuoto. Allo stesso modo, la custodia Leaf deve essere dichiarata prima del caso Nodo in modo che il campo 2 non sia accessibile su Leaf. Quindi la sicurezza del tipo non è quindi mantenuta dal linguaggio, ma piuttosto impone un carico cognitivo aggiuntivo sul programmatore. A proposito, sto prendendo questi esempi direttamente dalle pagine di Wikipedia.
Ovviamente, una langauge che digita le anatre 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 insieme di tensioni su cui lavorare durante la costruzione del software.