Quali sono gli usi dei tipi di dati algebrici?


16

Sto leggendo dei tipi di dati algebrici (grazie a Richard Minerich ho trovato questa eccellente spiegazione del concetto). Mentre penso di comprendere la nozione di tipi di somma, tipi di prodotto ecc., Ciò che non capisco è come i tipi di dati algebrici siano utili oltre a specificare la corrispondenza dei modelli. Quali altre cose si possono fare con l'ADT oltre la corrispondenza dei pattern?


EDIT: Non sto chiedendo cosa può fare uno sviluppatore con ADT che non può essere fatto con gli oggetti. Sto chiedendo se ci sono altre operazioni consentite da ADT; per esempio, si possono fare ulteriori ragionamenti sui tipi coinvolti se si utilizzano ADT? Gli ADT facilitano una sorta di analisi del tipo che non sarebbe possibile senza di loro?


2
Cosa puoi fare con gli oggetti tranne i metodi di chiamata?

1
ADT in realtà si riferisce al "tipo di dati astratto", non ai tipi di dati algebrici .
Rein Henrichs,

4
@Rein: può riferirsi a entrambi a seconda del contesto.
sepp2k,

4
@Rein: In effetti (cosa che trovo abbastanza sorprendente per essere onesti): Tuttavia l'articolo di Wikipedia per ADT elenca sia il tipo di dati astratto che il tipo di dati algebrico come possibili significati. E ADT è molto comunemente usato come abbreviazione di tipi di dati algebrici, ad esempio sulla mailing list di Haskell e sul canale IRC.
sepp2k,

1
@Rein, lo so - mi sono appena stancato di digitare "Algebraic Data Type" più e più volte e ho pensato che le persone sarebbero state in grado di capire a cosa mi riferivo dato il contesto.
Onorio Catenacci,

Risposte:


10

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 di decostruire i tipi di dati algebrici in modo da mantenere la sicurezza dei tipi. Considera la seguente implementazione di 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 di includere Vuoto prima di Leaf in modo che non si acceda a field1 su un albero vuoto. Allo stesso modo, il caso Leaf deve essere dichiarato prima del caso Node in modo che non sia possibile accedere a field2 su Leaf. Pertanto la sicurezza dei tipi non è quindi mantenuta dalla lingua, ma impone al programmatore un carico cognitivo aggiuntivo. A proposito, sto prendendo questi esempi direttamente dalle pagine di Wikipedia.

Certo, una lingua che digita l'anatra 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 set di tensioni con cui lavorare durante la costruzione di software.


9

Io non sono così sicuro la spiegazione è tutto quello eccellente.

I tipi di dati algebrici vengono utilizzati per creare strutture di dati, come elenchi e alberi.

Ad esempio gli alberi di analisi sono facilmente rappresentati con strutture di dati algebriche.

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

In realtà non ci vorrebbe molto di più per rappresentare il linguaggio C.

Ma davvero, puoi fare TUTTO con tipi di dati algebrici. Lisp dimostra che puoi fare tutto con coppie e ADT semplicemente fornendo un modo più granulare e sicuro per questo approccio.

Naturalmente, se chiedi "Cosa puoi fare con gli ADT, che non puoi fare con gli oggetti?", La risposta è "niente". Solo a volte (principalmente) troverai che le soluzioni sugli ADT sono significativamente meno dettagliate, mentre quelle basate sugli oggetti sono probabilmente più flessibili. Quindi, per metterlo in un albero di analisi rappresentato con ADT:

If(Call(Ident('likes_ADTs'),[Ident('you')]),
   Call(Ident('use_ADTs'),[Ident('you')]),
   Call(Ident('use_no_ADTs'),[Ident('you')]))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.