簡體   English   中英

forall a之間有什么區別? [a]和[forall a。一個]?

[英]What is the difference between forall a. [a] and [forall a. a]?

標題和標簽應該充分解釋這個問題。

標題和標簽應該充分解釋這個問題。

呃,不是真的。 您使用了標記existential-type ,但您提供的類型都不存在。

系統F.

這里已經有了一些很好的答案,所以我會采取不同的方法並且更加正式。 多態值本質上是類型上的函數,但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現在是四個參數的函數:兩種類型( ab ),一個函數和一個列表。 我們使用Λ(大寫lambda)在類型上編寫函數,並且像往常一樣使用λ函數。 包含Λ的項產生包含∀的類型,就像包含λ的項一樣,產生包含→的類型。 我使用符號@a (如在GHC Core中)來表示類型參數的應用。

因此,多態類型的值就像從類型到值的函數。 多態函數的調用者可以選擇一個類型參數,該函數必須符合。

∀a。 [一個]

那么,我們如何寫一個類型為∀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. a]是一系列功能。 我們可以盡可能少地提供:

term2 :: [∀a. a]
term2 = []

或者盡可能多:

term2 = [f, g, h]

但是我們對fgh選擇是什么?

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM