這是由於類型變量在標準Haskell中的範圍和量化方式。你可以讓第二個版本的工作,像這樣:
{-# LANGUAGE RankNTypes, ScopedTypeVariables #-}
module RigidProblem where
data Wrapped a = Wrapped a
alpha :: forall s. IO s -> IO()
alpha x = do
rv <- wrapit
return()
where
wrapit :: IO (Wrapped s)
wrapit = do
a <- x
return (Wrapped a)
有兩個變化:RankNTypes和ScopedTypeVariables語言擴展名被啓用,並明確forall s
中的alpha
類型簽名加入。兩個擴展中的第一個允許我們引入明確的forall s
,從而使s
進入alpha
主體的範圍內,而第二個擴展使得wrapit
的簽名不被類型推理引擎包含隱式forall s
- 取而代之的是,該簽名中的s
被用來命名一個類型變量,該變量應該在範圍內並且用它來標識。
在Haskell當前默認的情況下,如果我理解正確的,是所有的剛性類型變量(就是由程序員明確規定類型簽名發生類型變量)隱含量化,並不詞法範圍,所以不存在沒有辦法在內部作用域中提供的明確簽名中引用外部作用域中的剛性類型變量......(哦,麻煩了,我敢肯定有人可以比這更好地描述它)。無論如何,從類型檢查器的角度來看查看,s
在alpha
的簽名和wrapit
的簽名完全無關,不能統一 - 因此是錯誤。
查看來自GHC文檔的和來自Haskell Prime wiki的this page以獲取更多信息。
更新:我剛剛意識到我從來沒有解釋爲什麼第一個版本的作品。爲了完整起見:請注意,在第一個版本中,您可以使用t
代替wrapit
的s
的簽名,並且沒有任何更改。您甚至可以從where
塊中取出wrapit
,並使其成爲單獨的頂層功能。關鍵是它是一個多態函數,因此wrapit x
的類型由x
的類型決定。在第一個版本wrapit
的簽名中使用的類型變量與alpha
的簽名中使用的類型變量之間沒有任何關係,在這裏是有用的。對於第二個版本,這當然是不同的,並且你必須求助於上面提到的欺騙手段,以使wrapit
的s
與alpha
的s
一樣。
這個答案是絕對正確的,但我只是想強調一下重點:類型簽名中的變量的範圍僅限於該簽名,而不是相關定義的整體。所以你的兩個例子中的兩個不相關。 'ScopedTypeVariables'擴展名修改了用'forall'明確量化的變量的這個規則。 – 2010-02-19 05:59:45