Quais são os usos dos tipos de dados algébricos?

16

Estou lendo sobre tipos de dados algébricos (graças a Richard Minerich, encontrei esta excelente explicação do conceito). Embora eu entenda a noção de tipos de soma e tipos de produtos, etc., o que não entendo é como os Tipos de Dados Algébricos são úteis além de especificar a correspondência de padrões. Que outras coisas podemos fazer com os ADT além da correspondência de padrões?


Edição: Eu não estou perguntando o que um desenvolvedor pode fazer com ADT que não pode ser feito com objetos. Estou perguntando se existem outras operações que o ADT permite; por exemplo, alguém pode fazer um raciocínio adicional sobre os tipos envolvidos se os ADTs forem empregados? Os ADTs facilitam algum tipo de análise de tipo que não seria possível sem eles?

Onorio Catenacci
fonte
2
O que você pode fazer com objetos, exceto chamar métodos?
1
O ADT na verdade se refere a "tipo de dados abstratos", não a tipos de dados algébricos .
Rein Henrichs
4
@ Rein: Pode se referir a qualquer um, dependendo do contexto.
sepp2k 5/05
4
@ Rein: Realmente (o que acho bastante surpreendente para ser honesto): No entanto, o artigo da ADT na wikipedia lista os tipos de dados abstratos e tipos de dados algébricos como possíveis significados. E o ADT é muito comumente usado como uma abreviação de tipos de dados algébricos, por exemplo, na lista de discussão Haskell e no canal IRC.
sepp2k
1
@ Rein, eu sei - apenas me cansei de digitar "Algebraic Data Type" repetidamente e imaginei que as pessoas seriam capazes de entender o que eu estava me referindo, considerando o contexto.
Onorio Catenacci

Respostas:

10

Os tipos de dados algébricos são distintos, pois podem ser construídos a partir de vários tipos de "coisas". Por exemplo, uma Árvore pode conter nada (Vazio), Folha ou Nó.

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

Como um Nó é composto por duas Árvores, os tipos de dados algébricos podem ser recursivos.

A correspondência de padrões permite que os tipos de dados algébricos sejam desconstruídos de maneira a manter a segurança dos tipos. Considere a seguinte implementação de profundidade e seu equivalente em pseudocódigo:

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

comparado com:

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)

Isso tem a desvantagem de que o programador deve se lembrar de colocar Vazio antes de Folha para que o campo1 não seja acessado em uma árvore Vazia. Da mesma forma, o caso Leaf deve ser declarado antes do caso Node para que o campo2 não seja acessado no Leaf. Portanto, a segurança do tipo não é mantida pela linguagem, mas impõe carga cognitiva adicional ao programador. A propósito, eu estou pegando esses exemplos diretamente das páginas da Wikipedia.

Obviamente, um idioma de digitação de pato poderia fazer algo assim:

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

Portanto, os tipos de dados algébricos podem não ser estritamente melhores que os equivalentes a OOP, mas fornecem um conjunto diferente de tensões para trabalhar na construção de software.

Rein Henrichs
fonte
9

Eu não sou tão certo a explicação é tudo o que excelente.

Os tipos de dados algébricos são usados ​​para criar estruturas de dados, como listas e árvores.

Por exemplo, árvores de análise são facilmente representadas com estruturas de dados algébricas.

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

Na verdade, não seria necessário muito mais para representar a linguagem C.

Mas, na verdade, você pode fazer tudo com tipos de dados algébricos. O Lisp prova que você pode fazer tudo com pares e os ADTs simplesmente fornecem uma maneira mais granular e segura de digitar para essa abordagem.

Obviamente, se você perguntar: "O que você pode fazer com ADTs, que você não pode fazer com objetos?", A resposta é "nada". Somente às vezes (principalmente) você encontrará soluções em ADTs que são significativamente menos detalhadas, enquanto as baseadas em objetos são sem dúvida mais flexíveis. Então, para colocá-lo em uma árvore de análise representada por ADTs:

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