回答
我發現「提示」monad(來自「MonadPrompt」包)是一個非常有用的工具,在幾個地方(以及來自「操作」包的等效「程序」monad)與GADT(這是它使你能夠非常便宜且非常靈活地製作嵌入式語言。在Monad Reader issue 15中有一篇很好的文章叫做「三個Monad中的冒險」,它有一個很好的介紹Prompt monad以及一些現實中的GADTs
這是一個簡短的答案,但請參閱Haskell Wikibook。它會引導您通過一個良好類型的表達式樹的GADT,這是一個相當典型的例子:http://en.wikibooks.org/wiki/Haskell/GADT
GADT也用於實現類型相等:http://hackage.haskell.org/package/type-equality。我無法找到正確的論文供參考 - 現在這種技術已經很好地融入了民間傳說。然而,它在奧列格的無標籤內容中使用得相當好。參見例如有關編入GADT的部分。 http://okmij.org/ftp/tagless-final/#tc-GADT
它基本上是同樣例子的3倍,正如我在我的問題中告訴它的,這並不令人滿意。 – 2010-10-05 08:26:24
對不起,我沒有意識到我正在將您重定向到您的問題的同一鏈接 - 出於某種原因,我認爲您指的是GHC文檔。雖然我沒有看到你來自哪裏。如果你需要GADT,你可能會知道。在這一點上,wikibook總結了你將如何與其中一個合作。更具體的問題會有所幫助。 – sclv 2010-10-05 14:19:13
Is this [this](http://research.microsoft.com/en-us/um/people/akenn/generics/gadtoop.pdf)可能是您正在考慮的論文? – fotNelton 2014-01-12 06:49:53
我喜歡the GHC manual中的例子,它是GADT核心思想的一個快速演示:您可以將正在操作的語言的類型系統嵌入到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
,我們要反過來返回Bool
。
GADTs可以爲您提供比常規ADT更強大的強制類型擔保。例如,您可以強制二叉樹的2-3 treesthis implementation的類型系統級進行平衡,如:
{-# 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
每個節點都有一個類型編碼的深度,其中所有的葉子所在。一棵樹然後是 空樹,單值或未指定深度的節點,再次使用GADT的 。
data BTree a where
Root0 :: BTree a
Root1 :: a -> BTree a
RootN :: Node s a -> BTree a
該類型系統保證您只能構造平衡節點。 這意味着,像insert
對這些樹木實施操作的時候,你的 代碼類型檢查僅當它的結果總是平衡樹。
不適用於在檢查2-3樹的定義之前研究此代碼的人:Leaf2和Node2都有1個數據值; Leaf3和Node3都有2個數據值; Leaf2和Leaf3沒有子節點; Node2有2個孩子,Node3有3個孩子。 Leaf構造函數名稱中的數字有點混亂。 – misterbee 2014-01-18 22:36:56
GADTs從依賴輸入電感家庭弱近似語言,讓我們開始有代替。
感性的家庭處於依賴性類型語言中的核心數據類型的引入方法。例如,在阿格達您定義的自然數這樣
data Nat : Set where
zero : Nat
succ : Nat -> Nat
這是不是很花哨,它本質上是一樣的東西Haskell的定義
data Nat = Zero | Succ Nat
確實在GADT語法哈斯克爾形式更類似於
{-# LANGUAGE GADTs #-}
data Nat where
Zero :: Nat
Succ :: Nat -> Nat
所以,乍一看你可能會認爲GADTs是乾脆利落額外的語法。這只是冰山一角。
Agda有能力代表Haskell程序員不熟悉和陌生的各種類型。一個簡單的是有限集合的類型。這類型是這樣寫Fin 3
和代表組數字{0, 1, 2}
的。同樣,Fin 5
代表一組數字{0,1,2,3,4}
。
這應該是在這一點上很離奇。首先,我們指的是具有常規數字作爲「類型」參數的類型。其次,目前尚不清楚Fin n
代表集{0,1...n}
是什麼意思。在現實阿格達我們會做一些更強大,但它足以說,我們可以定義一個函數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
這讓我們看到,有兩個居民,也表明了類型的計算是如何發生的一點點。特別地,zerof
類型中的(n : Nat)
位反映了實際的值n
直到允許我們形成Fin (n+1)
爲任何n : Nat
的類型。之後,我們使用重複申請succf
將我們的Fin
值增加到正確的類型族索引(索引Fin
的自然數)。
什麼提供這些能力?坦率地說,依賴類型歸納系列與常規Haskell ADT之間存在許多差異,但我們可以專注於與理解GADT最相關的確切類型。
在GADT和歸納系列中,您有機會指定確切的您的構造函數的類型。這可能是無聊
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)
...
特別是,我們正在濫用修改的能力返回類型基於特定使用的值構造函數。這允許我們將一些價值信息反映到類型中,並生成更精細的指定(纖維)類型。
那麼我們可以用他們做什麼?那麼,我們可以用一點肘部潤滑脂produce Fin
in Haskell。簡潔它要求我們定義土黃的類型
data Z
data S a = S a
> undefined :: S (S (S Z)) -- 3
一個概念......然後GADT到成這些類型反映值...
data Nat where
Zero :: Nat Z
Succ :: Nat n -> Nat (S n)
...那麼我們可以利用這些建立Fin
就像我們在阿格達沒有...
data Fin n where
ZeroF :: Nat n -> Fin (S n)
SuccF :: Nat n -> Fin n -> Fin (S n)
最後,我們可以構建準確的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中,這是非常痛苦的,而且往往不能。
例如,它可能在阿格達的Fin
類型定義weaken
概念
weaken : (n <= m) -> Fin n -> Fin m
weaken = ...
,我們提供了一個非常有趣的第一值,證明了n <= m
這使我們能夠嵌入「的值小於n
」轉換成「小於m
」的值。在技術上,我們可以在Haskell中做同樣的事情,但它需要嚴重濫用類型序言。
因此,GADTs是一種類似於依賴型語言的歸納系列,它們更弱,更笨拙。我們爲什麼首先要在Haskell中使用它們?基本上,因爲並非所有的類型不變式都需要歸納系列的全部功能來表達,並且GADT選擇了表達性,Haskell中的可實現性和類型推斷之間的特定折衷。
有用的GADT表達式的一些示例是Red-Black Trees which cannot have the Red-Black property invalidated或simply-typed lambda calculus embedded as HOAS piggy-backing off the Haskell type system。
實際上,您還經常看到GADT用於其隱式存在上下文。例如,類型
data Foo where
Bar :: a -> Foo
隱式隱藏的方式,有時是方便的使用存在量化
> :t Bar 4 :: Foo
的a
型變量。如果仔細觀察,維基百科的HOAS示例將其用於App
構造函數中的a
類型參數。爲了在沒有GADT的情況下表達這個陳述將會是一團混亂的存在上下文,但是GADT語法使得它很自然。
爲什麼ZeroF需要一個參數n,因爲ZeroF在概念上是一個常量?爲什麼ZeroF構造'Fin(S n))'而不是'Fin n',這看起來更像是一個直接映射? (或者爲什麼連「Fin Z」都沒有?) – misterbee 2014-01-18 22:22:14
這有點奇怪,但是'Fin'類型的行爲與'Nat'類型不一樣。顯然,對於每個'n',只有一個'Nat n'的居民是'Fin n'的居民。因此,'ZeroF'實際上並不是一個常量。我可以說,瞭解更多關於「Fin」如何工作的最好辦法就是嘗試爲'n'的各種選擇構建'Fin n'的所有元素。這很違反直覺,但有一點練習是有道理的。 – 2014-01-18 22:34:13
@misterbee在Agda中,'Fin'構造函數的'n'參數通常是隱式的('zerof:{n:Nat} - > Fin(suc n)'),它在頁面上使'zerof'看起來有點更像是一個常數,而「Fin」看起來更像一個數字。 'sucf(sucf zerof)'是數字2,對於'n> = 3',可以居住任何'Fin n'。 – 2015-10-29 13:05:46
- 1. 真實世界中的Glassfish
- 2. OOP真實世界示例
- 3. 真實世界的目的
- 4. 真實世界對象
- 5. RabbitMQ真實世界場景
- 6. 真實世界的3D應用
- 7. C#代表真實世界用法
- 8. Mesos真實世界的用例
- 9. 真實世界的Jython應用程序
- 10. iPhone真實世界應用教程
- 11. RxJava的真實世界用例對象
- 12. 在真實世界中的oracle
- 13. 真實世界中的數據結構
- 14. 真實世界的接口實現
- 15. 在「真實世界」應用中使用ML
- 16. 使用@Cacheable註釋的真實世界DAO的最佳實踐
- 17. 接口的使用,實際的和真實世界的例子
- 18. 真實世界中使用的逆變和協方差.net
- 19. 在「真實世界」中使用反應式擴展
- 20. 真實世界中使用自定義.NET屬性
- 21. 在真實世界的Java程序中使用IPv6
- 22. 使用真實世界軸承在ARKit場景中定位SCNNode
- 23. 真實世界中使用綁定對象的紅寶石
- 24. 在C#中使用#define的真實世界示例?
- 25. 您使用了「堆棧」對象(.Net)的真實世界使用
- 26. TIS-100的真實世界模擬
- 27. swf to exe,真實世界體驗
- 28. OOPS的真實世界示例
- 29. 真實世界MVC - 處理形式
- 30. 真實世界的盲源分離
Nit:它是Generalized _Algebraic_ Data Types。抽象數據類型完全是另一回事。 – 2013-04-24 22:16:04
謝謝,修正,我在想什麼,我想知道。 – 2013-04-27 07:35:49