質問
くの、めんどくさいんだよな利用の一般化代数のデータタイプ?
の例では、 ウ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