[英]Why is `[1, "a"] :: [forall a. Show a => a]` not allowed?
[英]What is the difference between forall a. [a] and [forall a. a]?
標題和標簽應該充分解釋這個問題。
標題和標簽應該充分解釋這個問題。
呃,不是真的。 您使用了標記existential-type
,但您提供的類型都不存在。
這里已經有了一些很好的答案,所以我會采取不同的方法並且更加正式。 多態值本質上是類型上的函數,但Haskell的語法使得類型抽象和類型應用都隱含,這掩蓋了問題。 我們將使用System F的符號,它具有顯式類型抽象和類型應用程序。
例如,將編寫熟悉的map
功能
map :: ∀a. ∀b. (a → b) → [a] → [b]
map = Λa. Λb. λ(f :: a → b). λ(xs :: [a]). case xs of
[] → []
(y:ys) → f y : map @a @b f ys
map
現在是四個參數的函數:兩種類型( a
和b
),一個函數和一個列表。 我們使用Λ(大寫lambda)在類型上編寫函數,並且像往常一樣使用λ函數。 包含Λ的項產生包含∀的類型,就像包含λ的項一樣,產生包含→的類型。 我使用符號@a
(如在GHC Core中)來表示類型參數的應用。
因此,多態類型的值就像從類型到值的函數。 多態函數的調用者可以選擇一個類型參數,該函數必須符合。
那么,我們如何寫一個類型為∀a. [a]
的術語∀a. [a]
∀a. [a]
? 我們知道包含∀的類型來自包含Λ的術語:
term1 :: ∀a. [a]
term1 = Λa. ?
身體內有標記?
我們必須提供[a]
類型的術語。 也就是說,類型為∀a. [a]
的術語∀a. [a]
∀a. [a]
表示“給定任何類型a
,我會給你一個類型[a]
的列表”。
但是,我們對a
沒有具體了解,因為它是從外部傳入的一個論點。 所以我們可以返回一個空列表
term1 = Λa. []
或未定義的值
term1 = Λa. undefined
或僅包含未定義值的列表
term1 = Λa. [undefined, undefined]
但沒有其他的。
怎么樣[∀a. a]
[∀a. a]
,那么? 如果∀表示類型的函數,那么[∀a. a]
[∀a. a]
是一系列功能。 我們可以盡可能少地提供:
term2 :: [∀a. a]
term2 = []
或者盡可能多:
term2 = [f, g, h]
但是我們對f
, g
和h
選擇是什么?
f :: ∀a. a
f = Λa. ?
現在我們真的被困了。 我們必須提供類型a
的值,但我們對類型a
一無所知。 所以我們唯一的選擇是
f = Λa. undefined
所以我們對term2
的選擇看起來像
term2 :: [∀a. a]
term2 = []
term2 = [Λa. undefined]
term2 = [Λa. undefined, Λa. undefined]
等等,讓我們不要忘記
term2 = undefined
通用(∀)類型的值是從類型到值的函數。 存在(∃)類型的值是一對類型和值。
更具體地說:類型的值
∃x. T
是一對
(S, v)
其中S是一個類型,其中v :: T
,假設你將類型變量x
綁定到T
S
這是一個存在類型簽名和一些類型的術語:
term3 :: ∃a. a
term3 = (Int, 3)
term3 = (Char, 'x')
term3 = (∀a. a → Int, Λa. λ(x::a). 4)
換句話說,我們可以將任何我們喜歡的價值放入∃a. a
∃a. a
,只要我們將該值與其類型配對。
類型為∀a. a
的值的用戶 ∀a. a
∀a. a
處於有利位置; 他們可以使用類型應用程序@T
選擇他們喜歡的任何特定類型,以獲得類型為T
的術語。 類型為∀a. a
的生產者 ∀a. a
∀a. a
處於一個可怕的位置:他們必須准備好生產任何要求的類型,所以(如上一節所述)唯一的選擇是Λa. undefined
Λa. undefined
。
類型為∃a. a
的值的用戶 ∃a. a
∃a. a
處於可怕的位置; 里面的值是某種未知的特定類型,而不是靈活的多態值。 類型為∃a. a
的生產者 ∃a. a
∃a. a
處於有利位置; 正如我們上面所看到的那樣,他們可以將他們喜歡的任何價值都粘在一對
那么什么是一個不那么無用的存在主義? 如何將值與二元運算配對:
type Something = ∃a. (a, a → a → a, a → String)
term4_a, term4_b :: Something
term4_a = (Int, (1, (+) @Int , show @Int))
term4_b = (String, ("foo", (++) @Char, λ(x::String). x))
使用它:
triple :: Something → String
triple = λ(a, (x :: a, f :: a→a→a, out :: a→String)).
out (f (f x x) x)
結果:
triple term4_a ⇒ "3"
triple term4_b ⇒ "foofoofoo"
我們打包了一種類型和一些類型的操作。 用戶可以應用我們的操作但不能檢查具體值 - 我們不能在triple
中對x
進行模式匹配,因為它的類型(因此構造函數集)是未知的。 這有點像面向對象的編程。
使用∃和類型 - 值對的存在性的直接語法將非常方便。 UHC部分支持這種直接語法。 但GHC沒有。 要在GHC中引入存在性,我們需要定義新的“包裝”類型。
翻譯上面的例子:
{-# LANGUAGE ExistentialQuantification #-}
data Something = forall a. MkThing a (a -> a -> a) (a -> String)
term_a, term_b :: Something
term_a = MkThing 1 (+) show
term_b = MkThing "foo" (++) id
triple :: Something -> String
triple (MkThing x f out) =
out (f (f x x) x)
我們的理論處理有兩點不同。 類型應用程序,類型抽象和類型對再次是隱式的。 此外,包裝器使用forall
而不是exists
而令人困惑地寫入。 這引用了我們聲明一個存在類型的事實,但數據構造函數具有通用類型:
MkThing :: forall a. a -> (a -> a -> a) -> (a -> String) -> Something
通常,我們使用存在量化來“捕獲”類型類約束。 我們可以在這里做類似的事情:
data SomeMonoid = forall a. (Monoid a, Show a) => MkMonoid a
有關該理論的介紹,我強烈推薦Pierce的類型和編程語言 。 有關GHC中存在類型的討論,請參閱GHC手冊和Haskell維基 。
類型forall a. [a]
forall a. [a]
表示對於任何單一類型,它都是包含該類型的列表。 這也是普通[a]
意思,是[]
的類型,空列表數據構造函數。
類型[forall a. a]
[forall a. a]
意味着你有一個具有多態類型的值列表,也就是說,每個值都是任何可能類型的值,不一定與列表中的其他元素相同。 沒有可能的值可以具有類型forall a. a
forall a. a
,所以這也必須是一個空列表。
不同的是,雖然第一個可以用作任何類型的列表(根據定義,基本上),但后者不能用作任何具體類型的列表,因為沒有辦法固定它到任何一種類型。
為了解決標記 - 存在類型是在某個范圍內將被實例化為某種未知的具體類型的類型。 它可以是任何東西,所以用forall a. a
forall a. a
的上方。 為了確保具有存在類型的任何內容僅在實際類型可用的范圍內使用,編譯器會阻止存在類型“轉義”。
將forall
量詞視為lambda表達式可能有所幫助 - 它引入了一個新的類型變量並在某個范圍內綁定了該標識符。 在該范圍之外,標識符沒有意義,這就是為什么forall a. a
forall a. a
是好看不中用。
當用於類型時, forall
意味着交叉。 所以forall a. a
forall a. a
是所有類型或類似於Int ∩ String ∩ ...
的交集,它似乎給出了空集,但每個類型在Haskell中有一個名為bottom或⊥或undefined
的額外元素。 從此我們得到了那個forall a. a = {⊥}
forall a. a = {⊥}
。 實際上我們可以定義一個只包含底部的類型:
data Zero
在此設置之后,讓我們看一下以[forall a. a]
開頭]開頭的類型[forall a. a]
[forall a. a]
。 它定義的是底部列表或[Zero]
,其中包含元素[], [undefined], [undefined, undefined], ...
讓我們在ghci中檢查它:
> let l = [undefined, undefined]::[Zero]
> :t l
l :: [Zero]
以類似的方式發展forall a. [a]
forall a. [a]
是所有列表類型的交集,因為∩[a] = [∩a]
這又是[Zero]
。
要做最后的檢查,我們來定義:
type L = forall a. [a]
type U = [forall a. a]
並在ghci:
> let l2 = [undefined, undefined]::L
> let l3 = [undefined, undefined]::U
> :t l2
l2 :: [a]
> :t l3
l3 :: U
注意l2::[a]
,解釋是Haskell在所有多態類型之前放置了一個隱含的forall
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.