Algebraiczne typy danych różnią się tym, że można je zbudować z kilku rodzajów „rzeczy”. Na przykład Drzewo może nie zawierać niczego (Puste), Liścia lub Węzła.
data Tree = Empty
| Leaf Int
| Node Tree Tree
Ponieważ Węzeł składa się z dwóch Drzew, algebraiczne typy danych mogą być rekurencyjne.
Dopasowanie wzorca pozwala na dekonstruację typów danych algebraicznych w sposób zapewniający bezpieczeństwo typu. Rozważ następującą implementację głębokości i jej ekwiwalentu pseudokodu:
depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)
w porównaniu do:
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)
Ma to tę wadę, że programiści muszą pamiętać o opróżnieniu przed Leaf, aby pole1 nie było dostępne w pustym drzewie. Podobnie sprawa Liścia musi zostać zadeklarowana przed sprawą Węzła, aby pole 2 nie było dostępne w Liściu. W związku z tym język nie zapewnia bezpieczeństwa typu, ale raczej nakłada dodatkowe obciążenie poznawcze na programistę. Nawiasem mówiąc, pobieram te przykłady bezpośrednio ze stron wikipedii.
Oczywiście język kaczątkowy mógłby zrobić coś takiego:
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
Algebraiczne typy danych mogą nie być ściśle lepsze niż ich odpowiedniki OOP, ale zapewniają inny zestaw napięć do pracy przy tworzeniu oprogramowania.