你是正確的,因爲決議,這同forall
關鍵字做打電話ghci的。不過,這個原因需要一些解釋。如果你不關心那麼多的解釋,那就直接跳到最後。如果你還想要更多,請參閱GHC手冊的Syntax Extensions和Other Type System Extensions部分。
Haskell中由類型變量(例如data Heh a = Heh a
)參數化的類型聲明本質上是創建一個類型級函數(類型構造函數),它將一個類型作爲輸入並返回一個新類型。該的Heh
「種」(一種是我們如何劃分類型,如類型是如何分類的值)爲* -> *
,而Int
是一種*
和Heh Int
(適用於類型Int
的Heh
類型構造函數)是一種*
以及。因此,適用於*
一個* -> *
給出了另一個*
,這是那種正常的類型。
變量,無論是在值級別或類型級別,必須一些結合形式,它定義了程序文本名稱的特定變量是在其中結合的範圍(區域結合效果)的約束力。在價值層面,我們已經習慣了看到這些粘合劑所有的地方:一些例子在功能定義模式參數綁定,在let
表達式的綁定,並在lambda表達式的綁定(如\x -> x
結合中的名稱x
表達的主體)。
混亂始於型級變量;他們似乎根本沒有綁定,他們只是有在類型聲明的中間。這很大程度上是因爲Haskell的類型系統是從Hindley-Milner類型系統開始下降的,而Hindley-Milner類型系統根本不具有類型註釋,並且對於可能採用多種類型的表達式採用了不同的思維方式。多態類型的概念變得更加形式化,「polytyped」從辛德米爾納值成了「多態」和理論的語法類型版本包含的類型級粘合劑和相應的價值層次的粘合劑,將採取型和回報類型變量的表達式主體中的類型被替換的表達式。因爲這些綁定器在編譯時被完全解析,所以它們被排除在類型和值級別的語法之外。由於在原始系統中,活頁夾只能在類型聲明中的一個位置出現,因此不存在歧義。
如果你沒有按照這一切,不用擔心它;要點是,事情的發展是出於歷史原因,因爲基本概念以及它們在類型化函數式語言中實現的方式不斷髮展。
反正forall
是類型級變量粘合劑,有點像一個lambda表達式是一個值級變量粘合劑。沒有它,假設在每個綁定所有自由類型變量的類型級表達式的開始處有一個隱含的forall
。明確地綁定類型變量使得它們更「清楚」,它們是「普遍量化的」(如果你不熟悉普遍量化的概念,請參閱一階邏輯的介紹),並且也打開了「存在量化」的可能性「和更高排名的多態類型變量(擴展名爲RankNTypes
)。這似乎很奇怪,因爲存在量化通常表示通過「存在」的粘合劑,但如果你把一個forall a
內data
類型聲明不把a
進入活動範圍本身(例如data Foo b = forall a. Foo (a, a -> b)
),你得到相同的效果作爲存在量化a
(假設您啓用ExistentialQuantification
擴展,當然)。
但是你真正關心的是如何提高你的類型變量量化到整個instance
聲明的範圍;這是ScopedTypeVariables
擴展的用途。正如我之前所說的,Hindley-Milner類型系統最初並沒有使用綁定和自由類型變量的概念(有邏輯解析算法中扮演角色的類型方案(或多類型)和monotypes)甚至類型註釋。當首先加入類型註釋,它們代表「未知monotypes」和稱爲剛好在所述特定表達被註釋而不是一個範圍的結合構造像forall
或λ。因此,如果同一個名稱在類型環境中出現不止一次,則它們將被重命名。通過打開ScopedTypeVariables
,在明確綁定相同名稱的綁定範圍內表達的任何類型變量不再是未知單形變量(即來自錯誤消息的「剛性類型變量」),而是引用綁定類型變量該名稱最近的封閉類型聯編程序中的同名名稱。
總之,這裏的解決您的難題所需要的最終結果是:
{-# LANGUAGE ScopedTypeVariables #-}
data Heh a = Heh a
instance forall a. (Eq a) => Eq (Heh a) where
(Heh a1) == (Heh a2) = (a1 :: a) == a2
的forall
在實例聲明的範圍遍及實例聲明延伸,因爲ScopedTypeVariables
是積極的,在提及a
在a1
上的類型註釋是指與forall
中的相同的a
綁定。
如果你看的文檔,你會發現有結合類型的變量,以及一些其他的語法結構。在這種情況下,forall
實際上不是必需的,因爲在class
和instance
聲明頭部類和實例變量自動綁定中相同的方式的類型變量爲forall
確實當擴展是有效的。但是,如果你有需要跨功能的整個身體先限定類型聲明一個獨立的功能,你需要知道如何使用forall
得到你想要的類型變量的作用域。
是,涉及到'forall'因爲這個關鍵字明確地標誌着一個範圍 - 但我仍然認爲,範圍只標明一個函數體內,而不是where子句,但我對'forall'沒有專家。 – epsilonhalbe
@ epsilonhalbe對你有正確的答案;您需要'forall'和'ScopedTypeVariables'擴展名的組合。顯式'forall'還有其他用途(通過其他擴展),比如創建存在類型,或者只是爲了明確。 –
我推薦*總是*使用'ScopedTypeVariables'。 – dfeuer