簡體   English   中英

為什么id的類型不能專門用於(forall a.a - > a) - >(forall b.b - > b)?

[英]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 ,然后提供符合所選類型的兩個值xy 需要注意的重要事項是呼叫者選擇ab 因此調用者可以執行像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 Intx Char 這會強制調用者提供多態函數,因此像g length這樣的術語無效,因為length不適用於IntChar

考慮它的另一種方法是將fg的類型繪制為樹。 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的任務是找到一個最普通的任務acd使得兩個表達式是語法相等。 它最終發現需要統一cd 由於它們是單獨量化的,所以它處於死路並退出。

你可能會問這個問題,因為基本類型推理器 - 帶有歸屬(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.

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