質問

くの、めんどくさいんだよな利用の一般化代数のデータタイプ?

の例では、 ウwikibook が短すぎを与えてくれるものでありさえす洞察力の可能性GADT.

役に立ちましたか?

解決

このように誰にでも迅速-monad(MonadPrompt"パッケージ)から非常に有用なツールはいくつかの場所と同等の"プログラム"monadからは、"運用"パッケージです。合GADTsる方法で使用するものでしたが、できる組み込みの言語でもお安く非常にも柔軟にお応えいたします。が良記 Monadリーダーの問題15 という冒険にMonads"が実施されてきたことも導入の迅速なmonadとしてのリアルなGADTs.

他のヒント

GADTは、依存してタイプされた言語からの帰納的ファミリの弱い近似です。そのため、代わりに始めましょう。

帰納的ファミリは、依存してタイプされた言語のコアデータ型導入方法です。たとえば、AGDAでは、このような自然数を定義します

data Nat : Set where
  zero : Nat
  succ : Nat -> Nat 

それはあまり派手ではありません、それは本質的にHaskellの定義とまったく同じものです

data Nat = Zero | Succ Nat

そして確かにGADT構文では、Haskellフォームはさらに似ています

{-# LANGUAGE GADTs #-}

data Nat where
  Zero :: Nat
  Succ :: Nat -> Nat

したがって、最初は赤面しているとき、ガットは単なる余分な構文だと思うかもしれません。それは氷山の一角に過ぎません。


AGDAには、Haskellプログラマーになじみのないあらゆる種類のタイプを表現する能力があります。単純なものは、有限セットのタイプです。これ タイプ のように書かれています Fin 3 を表します セットする 数字の {0, 1, 2}. 。同じく、 Fin 5 数字のセットを表します {0,1,2,3,4}.

これはこの時点で非常に奇妙なはずです。まず、「タイプ」パラメーターとして通常の数字を持つタイプを参照しています。第二に、それが何を意味するのかは明らかではありません Fin n セットを表す {0,1...n}. 。実際のAGDAでは、もっと強力なことをするでしょうが、私たちが定義できると言うだけで十分です contains 働き

contains : Nat -> Fin n -> Bool
contains i f = ?

今、これは再び奇妙です。 contains のようなものです i < n, 、 しかし n タイプにのみ存在する値です Fin n そして、私たちはその分裂をそれほど簡単に渡ることができないはずです。定義はそれほど簡単ではないことが判明しましたが、これは誘導家族が依存してタイプされた言語で持っている力であり、それらの値に依存するタイプとタイプに依存する価値を導入します。


それが何であるかを調べることができます Fin それは、その定義を調べることでその財産を与えます。

data Fin : Nat -> Set where
  zerof : (n : Nat) -> Fin (succ n)
  succf : (n : Nat) -> (i : Fin n) -> Fin (succ n)

これには理解するには少しの作業が必要です。例として、タイプの値を構築してみてください Fin 2. 。これを行う方法はいくつかあります(実際、正確に2つあることがわかります)

zerof 1           : Fin 2
zerof 2           : Fin 3 -- nope!
zerof 0           : Fin 1 -- nope!
succf 1 (zerof 0) : Fin 2

これにより、2人の住民がいることがわかり、タイプの計算がどのように起こるかを少し示しています。特に、 (n : Nat) タイプのビット zerof 実際のものを反映しています 価値 n 私たちが形成することを可能にするタイプに上がります Fin (n+1) どちらのために n : Nat. 。その後、の繰り返しアプリケーションを使用します succf 私たちを増やすために Fin 正しいタイプファミリインデックス(インデックスを付ける自然数への値 Fin).

これらの能力を提供するものは何ですか?正直なところ、依存してタイプされた誘導家族と通常のHaskell ADTの間には多くの違いがありますが、GADTを理解することに最も関連する正確な家族に焦点を当てることができます。

GADTSや誘導家族では、 ちょうど コンストラクターのタイプ。これは退屈かもしれません

data Nat where
  Zero :: Nat
  Succ :: Nat -> Nat

または、より柔軟でインデックス付きタイプがある場合、さまざまな、より興味深いリターンタイプを選択できます

data Typed t where
  TyInt  :: Int                -> Typed Int
  TyChar :: Char               -> Typed Char
  TyUnit ::                       Typed ()
  TyProd :: Typed a -> Typed b -> Typed (a, b)
  ...

特に、私たちは、 特に 使用されるバリューコンストラクター。これにより、いくつかの価値情報をタイプに反映し、より細かく指定された(繊維)と入力することができます。


それで、私たちは彼らと何ができますか?まあ、少しの肘のグリースで私たちはできる 生産 Fin Haskellで. 。簡潔に言えば、タイプの自然の概念を定義する必要があります

data Z
data S a = S a

> undefined :: S (S (S Z))  -- 3

...次に、これらのタイプに値を反映するガット...

data Nat where
  Zero :: Nat Z
  Succ :: Nat n -> Nat (S n)

...その後、これらを使用して構築できます Fin AGDAでやったように...

data Fin n where
  ZeroF :: Nat n -> Fin (S n)
  SuccF :: Nat n -> Fin n -> Fin (S n)

そして最後に、正確に2つの値を構築できます Fin (S (S Z))

*Fin> :t ZeroF (Succ Zero)
ZeroF (Succ Zero) :: Fin (S (S Z))

*Fin> :t SuccF (Succ Zero) (ZeroF Zero)
SuccF (Succ Zero) (ZeroF Zero) :: Fin (S (S Z))

しかし、私たちは帰納的家族に対して多くの利便性を失っていることに注意してください。たとえば、タイプで通常の数値リテラルを使用することはできません(とにかくAGDAの技術的には単なるトリックですが)。個別の「タイプNAT」と「値NAT」を作成し、GADTを使用してそれらをリンクする必要があります。また、やがて、タイプレベルの数学はAGDAでは苦痛ですが、それを行うことができることもわかりました。 Haskellでは、信じられないほど苦痛で、しばしばできません。

たとえば、aを定義することが可能です weaken AGDAの概念 Fin タイプ

weaken : (n <= m) -> Fin n -> Fin m
weaken = ...

私たちが非常に興味深い最初の価値を提供する場所、その証拠 n <= m これにより、「値より少ない値」を埋め込むことができます n「一連のセットに」値よりも少ない m"。ハスケルでも技術的にも同じことができますが、型クラスのプロログの激しい乱用が必要です。


したがって、GADTは、より弱くて不器用な、依存してタイプされた言語の誘導家族の類似点です。そもそもなぜハスケルでそれらを望んでいるのですか?

基本的に、すべてのタイプの不変剤が誘導家族の完全な力を表現し、ガドが表現力、Haskellの実装可能性、およびタイプの推論との間の特定の妥協を選択する必要があるわけではありません。

有用なガットの表現の例がいくつかあります 赤黒の特性を無効にすることができない赤黒の木 また HaasがHaskellタイプのシステムから豚をぶらぶらするように埋め込まれたシンプルなタイプのラムダ計算.

実際には、GADTが暗黙の実存的コンテキストに使用していることもよくあります。たとえば、タイプ

data Foo where
  Bar :: a -> Foo

暗黙的に隠します a 実存的な定量化を使用して、タイプ変数を使用します

> :t Bar 4 :: Foo

時には便利な方法で。あなたが注意深く見ると、ウィキペディアのHOASの例がこれを使用します a のタイプパラメーター App コンストラクタ。ガットなしでその声明を表現することは、実存的な文脈の混乱になるでしょうが、ガットの構文はそれを自然にします。

GADTは、通常のADTよりも強力なタイプの強制保証を提供できます。たとえば、次のように、タイプシステムレベルでバイナリツリーを強制することができます。 この実装2-3木:

{-# LANGUAGE GADTs #-}

data Zero
data Succ s = Succ s

data Node s a where
    Leaf2 :: a -> Node Zero a
    Leaf3 :: a -> a -> Node Zero a
    Node2 :: Node s a -> a -> Node s a -> Node (Succ s) a
    Node3 :: Node s a -> a -> Node s a -> a -> Node s a -> Node (Succ s) a

各ノードには、すべての葉が存在するタイプエンコードの深さがあります。ツリーは、空のツリー、シングルトン値、または再びガッドを使用して不特定の深さのノードです。

data BTree a where
    Root0 :: BTree a
    Root1 :: a -> BTree a
    RootN :: Node s a -> BTree a

タイプシステムは、バランスの取れたノードのみを構築できることを保証します。これは、ような操作を実装するときにそれを意味します insert そのような木では、その結果が常にバランスの取れたツリーである場合にのみ、コードタイプチェックがあります。

私は例が好きです GHCマニュアル. 。これは、Core Gadtのアイデアの簡単なデモです。Haskellのタイプシステムに操作している言語のタイプシステムを埋め込むことができるということです。これにより、Haskell関数は、構文ツリーが十分なタイプのプログラムに対応することを想定し、強制することができます。

定義するとき Term, 、どんなタイプを選んでもかまいません。書くことができます

data Term a where
  ...
  IsZero :: Term Char -> Term Char

また

  ...
  IsZero :: Term a -> Term b

との定義 Term まだ通過します。

一度したいのです 計算します Term, 、定義するなど eval, 、タイプが重要です。私たちは持っている必要があります

  ...
  IsZero :: Term Int -> Term Bool

再帰的な呼び出しが必要だからです eval 返品します Int, 、そして順番にaを返したいと思います Bool.

これは簡単な答えですが、Haskell Wikibookに相談してください。それは、よく標準的な例です。 http://en.wikibooks.org/wiki/haskell/gadt

GADTは、タイプの平等を実装するためにも使用されます。 http://hackage.haskell.org/package/type-equality. 。このために参照するのに適した紙を見つけることができません。このテクニックは、今までに民俗学に順調に進んでいます。ただし、Olegのタイプされたタグレスのものでは、非常によく使用されています。たとえば、タイプされたコンピレーションのセクションをGADTSに参照してください。 http://okmij.org/ftp/tagless-final/#tc-gadt

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top