Was sind die Verwendungen von algebraischen Datentypen?


16

Ich lese über algebraische Datentypen (dank Richard Minerich fand ich diese hervorragende Erklärung des Konzepts). Ich verstehe zwar den Begriff der Summentypen und Produkttypen usw., verstehe aber nicht ganz, wie nützlich algebraische Datentypen sind, abgesehen von der Angabe des Mustervergleichs. Welche anderen Dinge kann man mit ADTs jenseits des Mustervergleichs tun?


EDIT: Ich frage nicht, was ein Entwickler mit ADTs machen kann, die nicht mit Objekten gemacht werden können. Ich frage, ob es andere Operationen gibt, die ADT zulassen. Kann man zum Beispiel zusätzliche Überlegungen zu den beteiligten Typen anstellen, wenn ADTs eingesetzt werden? Ermöglichen ADTs eine Art von Typanalyse, die ohne sie nicht möglich wäre?


2
Was können Sie mit Objekten tun, außer Methoden aufzurufen?

1
ADT bezieht sich tatsächlich auf "abstrakten Datentyp", nicht auf algebraische Datentypen.
Rein Henrichs

4
@Rein: Es kann sich je nach Kontext auf beides beziehen.
SEPP2K

4
@Rein: Das tut es in der Tat (was ich ehrlich gesagt ziemlich überraschend finde): Der Wikipedia-Artikel für ADT listet jedoch sowohl den abstrakten Datentyp als auch den algebraischen Datentyp als mögliche Bedeutungen auf. Und ADT wird sehr häufig als Abkürzung für algebraische Datentypen verwendet, beispielsweise für die Haskell-Mailingliste und den IRC-Kanal.
SEPP2K,

1
@Rein, ich weiß - habe es einfach satt, immer wieder "Algebraic Data Type" einzugeben, und ich dachte, die Leute könnten verstehen, worauf ich mich beziehe, wenn der Kontext gegeben ist.
Onorio Catenacci

Antworten:


10

Algebraische Datentypen unterscheiden sich darin, dass sie aus mehreren Arten von "Dingen" erstellt werden können. Beispielsweise kann ein Baum entweder nichts (Leer), ein Blatt oder einen Knoten enthalten.

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

Da ein Knoten aus zwei Bäumen besteht, können algebraische Datentypen rekursiv sein.

Mit Pattern Matching können algebraische Datentypen so dekonstruiert werden , dass die Typensicherheit erhalten bleibt. Betrachten Sie die folgende Implementierung der Tiefe und ihres Pseudocode-Äquivalents:

depth :: Tree -> Int
depth Empty = 0
depth (Leaf n) = 1
depth (Node l r) = 1 + max (depth l) (depth r)

verglichen mit:

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)

Dies hat den Nachteil, dass der Programmierer daran denken muss, Leer vor Blatt zu schreiben, damit auf Feld1 in einem leeren Baum nicht zugegriffen wird. Ebenso muss der Blattfall vor dem Knotenfall deklariert werden, damit auf Feld2 in Blatt nicht zugegriffen wird. Somit wird die Typensicherheit nicht durch die Sprache aufrechterhalten, sondern erlegt dem Programmierer eine zusätzliche kognitive Belastung auf. Übrigens greife ich diese Beispiele direkt von den Wikipedia-Seiten.

Natürlich könnte eine Sprache, die Enten tippt, so etwas tun:

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

Daher sind algebraische Datentypen möglicherweise nicht unbedingt besser als ihre OOP-Entsprechung, bieten jedoch eine andere Menge von Spannungen, mit denen beim Erstellen von Software gearbeitet werden kann.


9

Ich bin nicht so sicher , dass die Erklärung alle ist , dass ausgezeichnet.

Algebraische Datentypen werden zum Erstellen von Datenstrukturen wie Listen und Bäumen verwendet.

Zum Beispiel lassen sich Analysebäume leicht mit algebraischen Datenstrukturen darstellen.

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>
                | ...

Es würde eigentlich nicht viel mehr brauchen, um die C-Sprache darzustellen.

Mit algebraischen Datentypen können Sie jedoch ALLES tun. Lisp beweist, dass man alles mit Paaren machen kann und ADTs bieten einfach einen präziseren und typsicheren Weg zu diesem Ansatz.

Wenn Sie fragen, was Sie mit ADTs tun können, was Sie nicht mit Objekten tun können, lautet die Antwort natürlich "nichts". Nur manchmal (meistens) werden Sie feststellen, dass die Lösungen für ADTs deutlich weniger ausführlich sind, während die auf Objekten basierenden Lösungen möglicherweise flexibler sind. Um es in einen mit ADTs dargestellten Analysebaum zu setzen:

If(Call(Ident('likes_ADTs'),[Ident('you')]),
   Call(Ident('use_ADTs'),[Ident('you')]),
   Call(Ident('use_no_ADTs'),[Ident('you')]))
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.