代数データ型の用途は何ですか?


16

私は代数データ型について読んでいます(Richard Minerichのおかげで、この概念のこの素晴らしい説明を見つけました)。私は和の種類や製品の種類などの概念を理解していると思いますが、パターンマッチングを指定する以外に、代数的データ型がどのように役立つかについてはあまり理解していません。パターンマッチングを超えたADTで他にできることは何ですか?


編集:私は開発者がオブジェクトで行うことができないADTで何ができるかを尋ねていません。ADTで許可されている他の操作があるかどうかを尋ねています。たとえば、ADTが採用されている場合、関係するタイプについて追加の推論を行うことができますか?ADTは、それなしでは不可能な、ある種の型分析を容易にしますか?


2
メソッドの呼び出し以外のオブジェクトで何ができますか?

1
ADTは実際には、代数的データ型ではなく、「抽象データ型」を指します。
ラインヘンリッヒス

4
@Rein:コンテキストに応じてどちらかを参照できます。
sepp2k

4
@Rein:確かにそうです(正直なところ、非常に驚​​くべきことです):ただし、ADTのウィキペディアの記事では、Abstract Data TypeとAlgebraic Data Typeの両方を可能な意味としてリストしています。また、ADTは、HaskellメーリングリストやIRCチャネルなどの代数的データ型の略語として非常に一般的に使用されています。
sepp2k

1
@Rein、私は知っている-「代数データ型」を何度も入力するのにうんざりして、私は人々がコンテキストを参照して何を言っているのか理解できるだろうと思った。
オノリオカテナッチ

回答:


10

代数データ型は、いくつかのタイプの「もの」から構築できるという点で異なります。たとえば、ツリーには何も(空)、リーフ、またはノードを含めることができます。

data Tree = Empty
          | Leaf Int
          | Node Tree Tree

Nodeは2つのTreeで構成されているため、代数的なデータ型は再帰的になります。

パターンマッチングにより、代数データ型を型安全性を維持する方法で分解できます。次の深さの実装とそれに相当する擬似コードを検討してください。

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

に比べ:

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)

これには、プログラマが空のツリーでfield1にアクセスしないように、リーフの前に空を大文字にすることを忘れてはならないという欠点があります。同様に、リーフケースは、ノードケースの前に宣言して、フィールド2がリーフでアクセスされないようにする必要があります。したがって、型安全性は言語によって維持されず、プログラマーに追加の認知的負荷を課します。ところで、ウィキペディアのページから直接これらの例を取得しています。

もちろん、ダックタイピング言語は次のようなことができます:

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

そのため、代数的データ型は、同等のOOPより厳密に優れているわけではありませんが、ソフトウェアを構築する際に動作するさまざまな緊張を提供します。


9

私はしませんので、必ず説明が全てですよという優れ。

代数データ型は、リストやツリーなどのデータ構造を作成するために使用されます。

たとえば、解析ツリーは代数データ構造で簡単に表現できます。

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

実際には、C言語を表すのにそれほど多くは必要ありません。

しかし、実際には、代数データ型を使用してすべてを実行できます。Lispが証明しているように、ペアですべてを行うことができ、ADTはこのアプローチに対してより詳細でタイプセーフな方法を提供するだけです。

もちろん、「ADTでできること、オブジェクトではできないこと」と尋ねると、答えは「何もありません」です。ADTの解決策の冗長性が大幅に低くなる一方で、オブジェクトに基づく解決策の方が間違いなく柔軟性が高いことが、たまに(大部分)わかるだけです。そのため、ADTで表される解析ツリーに配置するには:

If(Call(Ident('likes_ADTs'),[Ident('you')]),
   Call(Ident('use_ADTs'),[Ident('you')]),
   Call(Ident('use_no_ADTs'),[Ident('you')]))
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.