![](/img/trans.png)
[英]Why is forall a. a not considered a subtype of Int while I can use an expression of type forall a. a anywhere one of type Int is expected?
[英]Why can't the type of id be specialised to (forall a. a -> a) -> (forall b. b -> b)?
在Haskell中采用簡單的身份功能,
id :: forall a. a -> a
鑒於Haskell據說支持不可預測的多態性,我應該能夠通過類型歸屬將id
“限制”到類型(forall a. a -> a) -> (forall b. b -> b)
a。a (forall a. a -> a) -> (forall b. b -> b)
似乎是合理的。 但這不起作用:
Prelude> id :: (forall a. a -> a) -> (forall b. b -> b)
<interactive>:1:1:
Couldn't match expected type `b -> b'
with actual type `forall a. a -> a'
Expected type: (forall a. a -> a) -> b -> b
Actual type: (forall a. a -> a) -> forall a. a -> a
In the expression: id :: (forall a. a -> a) -> (forall b. b -> b)
In an equation for `it':
it = id :: (forall a. a -> a) -> (forall b. b -> b)
當然可以使用所需的簽名定義一個新的,受限制的身份函數形式:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId x = x
但是,根據通用id
定義它不起作用:
restrictedId :: (forall a. a -> a) -> (forall b. b -> b)
restrictedId = id -- Similar error to above
那么這里發生了什么? 看起來它可能與難以理解的困難有關,但啟用-XImpredicativeTypes
沒有任何區別。
為什么它期待一種類型
(forall a. a -> a) -> b -> b
我認為類型forall b.(forall a. a -> a) -> b -> b
等同於你給出的類型。 它只是它的規范表示,其中forall盡可能向左移動。
並且它不起作用的原因是給定類型實際上比id :: forall c的類型更多態。 c - > c,它要求參數和返回類型相等。 但是你的類型中的forall有效地禁止與任何其他類型統一。
你是絕對正確的forall b. (forall a. a -> a) -> b -> b
forall b. (forall a. a -> a) -> b -> b
不等同於(forall a. a -> a) -> (forall b. b -> b)
。
除非另有注釋,否則類型變量在最外層進行量化。 所以(a -> a) -> b -> b
是(forall a. (forall b. (a -> a) -> b -> b))
簡寫。 在系統F中,類型抽象和應用程序是明確的,這描述了像f = Λa. Λb. λx:(a -> a). λy:b. xy
這樣的術語f = Λa. Λb. λx:(a -> a). λy:b. xy
f = Λa. Λb. λx:(a -> a). λy:b. xy
f = Λa. Λb. λx:(a -> a). λy:b. xy
。 對於不熟悉符號的人來說, Λ
是一個lambda,它將一個類型作為參數,與λ
不同,它以一個術語作為參數。
f
的調用者首先提供類型參數a
,然后提供類型參數b
,然后提供符合所選類型的兩個值x
和y
。 需要注意的重要事項是呼叫者選擇a
和b
。 因此調用者可以執行像f String Int length
這樣的應用程序,例如生成一個術語String -> Int
。
使用-XRankNTypes
可以通過顯式放置通用量詞來注釋術語,它不必位於最外層。 您的restrictedId
術語類型(forall a. a -> a) -> (forall b. b -> b)
可以在系統F中大致舉例說明為g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
g = λx:(forall a. a -> a). if (x Int 0, x Char 'd') > (0, 'e') then x else id
。 注意g
被調用者如何通過首先用類型實例化x
來將x
應用於0
和'e'
。
但在這種情況下,調用者不能像之前用f
那樣選擇類型參數。 你會注意到lambda中的應用程序x Int
和x Char
。 這會強制調用者提供多態函數,因此像g length
這樣的術語無效,因為length
不適用於Int
或Char
。
考慮它的另一種方法是將f
和g
的類型繪制為樹。 f
的樹具有通用量詞作為根,而g
的樹具有箭頭作為根。 為了獲得f
的箭頭,調用者實例化兩個量詞。 使用g
,它已經是箭頭類型,調用者無法控制實例化。 這會強制調用者提供多態參數。
最后,請原諒我做作的例子。 Gabriel Scherer描述了更高等級多態性在System F的中等實際使用中對ML的更實際用途。 您還可以參考TAPL的第23和30章,或瀏覽編譯器擴展的文檔,以查找更高詳細信息或更高級別多態性的實際示例。
我不是關於不可預測類型的專家,所以這既是一個潛在的答案,也是嘗試從評論中學習一些東西。
專業化沒有意義
\/ a . a -> a (1)
至
(\/ a . a -> a) -> (\/ b . b -> b) (2)
我不認為不可預測的類型是允許它的理由。 量詞通常具有使(2)不等價集的左側和右側表示的類型的效果。 然而a -> a
in(1)意味着左側和右側是等價的集合。
例如,你可以將(2)具體化為(int - > int) - >(string - > string)。 但是通過任何系統,我知道這不是(1)所代表的類型。
錯誤消息看起來像是由Haskel類型推理器嘗試統一id
的類型而產生的
\/ a . a -> a
與你給出的類型
\/ c . (c -> c) -> \/ d . (d -> d)
在這里,為了清晰起見,我將量化變量統一起來。
類型inferencer的任務是找到一個最普通的任務a
, c
和d
使得兩個表達式是語法相等。 它最終發現需要統一c
和d
。 由於它們是單獨量化的,所以它處於死路並退出。
你可能會問這個問題,因為基本類型推理器 - 帶有歸屬(c -> c) -> (d -> d)
- 只需要向前推進並設置c == d
。 結果類型將是
(c -> c) -> (c -> c)
這只是簡寫
\/c . (c -> c) -> (c -> c)
對於x = x
的類型,這可證明是最不常見的類型(類型理論上最小上界)表達式,其中x
被約束為具有相同域和共域的函數。
給出的“restrictededId”的類型在實際意義上過於籠統。 雖然它永遠不會導致運行時類型錯誤,但是你給它的表達式描述了很多類型 - 比如前面提到的(int -> int) -> (string -> string)
- 即使你的操作也是不可能的類型會允許他們。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.