Les types de données algébriques sont distincts en ce qu'ils peuvent être construits à partir de plusieurs types de «choses». Par exemple, un arbre peut contenir soit rien (vide), une feuille ou un nœud.
data Tree = Empty
| Leaf Int
| Node Tree Tree
Puisqu'un nœud est composé de deux arbres, les types de données algébriques peuvent être récursifs.
La correspondance de modèles permet de déconstruire les types de données algébriques de manière à maintenir la sécurité des types. Considérez l'implémentation suivante de depth et de son équivalent pseudocode:
depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)
par rapport à:
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)
Cela présente l'inconvénient que le programmeur doit se rappeler de mettre en cas Empty avant Leaf afin que field1 ne soit pas accessible sur une arborescence vide. De même, le cas Leaf doit être déclaré avant le cas Node afin que le champ2 ne soit pas accessible sur Leaf. Ainsi, la sécurité des types n'est donc pas maintenue par le langage mais impose plutôt une charge cognitive supplémentaire au programmeur. Soit dit en passant, je prends ces exemples directement à partir des pages wikipedia.
Bien sûr, une jauge de type canard pourrait faire quelque chose comme ceci:
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
Ainsi, les types de données algébriques peuvent ne pas être strictement meilleurs que leur équivalent OOP, mais ils fournissent un ensemble de tensions différent avec lequel travailler lors de la construction d'un logiciel.