Quelles sont les utilisations des types de données algébriques?

16

Je lis sur les types de données algébriques (grâce à Richard Minerich, j'ai trouvé cette excellente explication du concept). Bien que je pense comprendre la notion de types de somme et de types de produits, etc., ce que je ne comprends pas vraiment, c'est comment les types de données algébriques sont utiles au-delà de la spécification de la correspondance de modèles. Quelles autres choses peut-on faire avec l'ADT au-delà de la correspondance de motifs?


EDIT: Je ne demande pas ce qu'un développeur peut faire avec des ADT qui ne peuvent pas être faits avec des objets. Je demande s'il y a d'autres opérations autorisées par ADT; par exemple, peut-on faire un raisonnement supplémentaire sur les types impliqués si des ADT sont employés? Les ADT facilitent-ils une sorte d'analyse de type qui ne serait pas possible sans eux?

Onorio Catenacci
la source
2
Que pouvez-vous faire avec des objets à l'exception des méthodes d'appel?
1
ADT se réfère en fait à un "type de données abstrait" et non à des types de données algébriques .
Rein Henrichs
4
@Rein: Il peut faire référence à l'un ou l'autre selon le contexte.
sepp2k
4
@Rein: En effet (ce que je trouve assez surprenant pour être honnête): Cependant, l'article de wikipedia pour ADT répertorie à la fois le type de données abstrait et le type de données algébrique comme significations possibles. Et ADT est très couramment utilisé comme abréviation pour les types de données algébriques, par exemple sur la liste de diffusion Haskell et le canal IRC.
sepp2k
1
@Rein, je sais - je me suis juste lassé de taper "Algebraic Data Type" encore et encore et j'ai pensé que les gens seraient en mesure de comprendre à quoi je faisais référence compte tenu du contexte.
Onorio Catenacci

Réponses:

10

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.

Rein Henrichs
la source
9

Je ne suis pas sûr que l'explication soit si excellente.

Les types de données algébriques sont utilisés pour créer des structures de données, telles que des listes et des arbres.

Par exemple, les arbres d'analyse sont facilement représentés avec des structures de données algébriques.

data BinOperator = Add
                 | Subtr
                 | Div
                 | Mult
                 | Mod
                 | Eq
                 | NotEq
                 | GreaterThan
                 | LogicAnd
                 | LogicOr
                 | BitAnd
                 | BitOr
                 | ...

data UnOperator = Negate
                | Not
                | Increment
                | Decrement
                | Complement
                | Ref
                | DeRef


data Expression = Empty
                | IntConst Int
                | FloatConst Float
                | StringConst String
                | Ident String
                | BinOp BinOperator Expression Expression
                | UnOp UnOperator Expression Bool //prefix or not
                | If Expression Expression Expression
                | While Expression Expression Bool //while vs. do while
                | Block List<Expression>
                | Call Expression List<Expression>
                | ...

Il ne faudrait pas vraiment beaucoup plus pour représenter le langage C.

Mais vraiment, vous pouvez tout faire avec des types de données algébriques. Lisp prouve que vous pouvez tout faire avec des paires et des ADT.

Bien sûr, si vous demandez: "Que pouvez-vous faire avec les ADT, que vous ne pouvez pas faire avec les objets?", La réponse est "rien". Ce n'est que parfois (principalement) que vous trouverez que les solutions sur les ADT sont beaucoup moins verbeuses, tandis que celles basées sur des objets sont sans doute plus flexibles. Donc, pour le mettre dans un arbre d'analyse représenté avec des ADT:

If(Call(Ident('likes_ADTs'),[Ident('you')]),
   Call(Ident('use_ADTs'),[Ident('you')]),
   Call(Ident('use_no_ADTs'),[Ident('you')]))
back2dos
la source