2010-02-18 57 views
9

這工作:這是如何構成一個Haskell剛性類型的錯誤?

data Wrapped a = Wrapped a 

alpha :: IO s -> IO() 
alpha x = do 
    rv <- wrapit x 
    return() 
    where  
     wrapit :: IO s -> IO (Wrapped s) 
     wrapit x' = do 
      a <- x' 
      return (Wrapped a) 

這不:

data Wrapped a = Wrapped a 

alpha :: IO s -> IO() 
alpha x = do 
    rv <- wrapit 
    return() 
    where  
     wrapit :: IO (Wrapped s) 
     wrapit = do 
      a <- x 
      return (Wrapped a) 

爲什麼?

回答

19

這是由於類型變量在標準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當前默認的情況下,如果我理解正確的,是所有的剛性類型變量(就是由程序員明確規定類型簽名發生類型變量)隱含量化,並詞法範圍,所以不存在沒有辦法在內部作用域中提供的明確簽名中引用外部作用域中的剛性類型變量......(哦,麻煩了,我敢肯定有人可以比這更好地描述它)。無論如何,從類型檢查器的角度來看查看,salpha的簽名和wrapit的簽名完全無關,不能統一 - 因此是錯誤。

查看來自GHC文檔的和來自Haskell Prime wiki的this page以獲取更多信息。

更新:我剛剛意識到我從來沒有解釋爲什麼第一個版本的作品。爲了完整起見:請注意,在第一個版本中,您可以使用t代替wrapits的簽名,並且沒有任何更改。您甚至可以從where塊中取出wrapit,並使其成爲單獨的頂層功能。關鍵是它是一個多態函數,因此wrapit x的類型由x的類型決定。在第一個版本wrapit的簽名中使用的類型變量與alpha的簽名中使用的類型變量之間沒有任何關係,在這裏是有用的。對於第二個版本,這當然是不同的,並且你必須求助於上面提到的欺騙手段,以使wrapitsalphas一樣。

+5

這個答案是絕對正確的,但我只是想強調一下重點:類型簽名中的變量的範圍僅限於該簽名,而不是相關定義的整體。所以你的兩個例子中的兩個不相關。 'ScopedTypeVariables'擴展名修改了用'forall'明確量化的變量的這個規則。 – 2010-02-19 05:59:45

6
上述

米哈爾Marczyk的答案是正確的,但值得注意的是,如果你刪除wrapit函數的類型簽名中的第二個版本確實工作:

data Wrapped a = Wrapped a 

alpha :: IO s -> IO() 
alpha x = do 
    rv <- wrapit 
    return() 
    where 
     -- No type signature here! 
     wrapit = do 
      a <- x 
      return (Wrapped a) 

也就是說,這個問題是不是與代碼本身;這就是Haskell 98不允許你爲wrapit函數寫一個類型簽名,因爲它包含一個由其上下文綁定的類型變量(外部函數alpha),而H98沒有辦法表達它。正如Michał所說,啓用ScopedTypeVariables可以讓你這麼做。