2014-01-19 156 views
5

我編寫了一個Haskell程序,並得到了編譯錯誤,我不明白。瞭解一個Haskell類型歧義的案例

程序應該:

  • 獲取命令行參數
  • 拼接標記化參數回單String
  • 閱讀StringNestedList數據類型
  • 拼合NestedListList
  • 打印List

不幸的是,由於類型不明確,它不會編譯。

Haskell代碼:

{- 
    Run like this: 
    $ ./prog List [Elem 1, List [Elem 2, List [Elem 3, Elem 4], Elem 5]] 
    Output: [1,2,3,4,5] 
-} 
import System.Environment 
import Data.List 

data NestedList a = Elem a | List [NestedList a] 
    deriving (Read) 

main = do 
    args <- getArgs 
    print . flatten . read $ intercalate " " args 

flatten :: NestedList a -> [a] 
flatten (Elem x) = [x] 
flatten (List x) = concatMap flatten x 

編譯錯誤:

prog.hs:8:21: 
    Ambiguous type variable `a0' in the constraints: 
     (Read a0) arising from a use of `read' at prog.hs:8:21-24 
     (Show a0) arising from a use of `print' at prog.hs:8:3-7 
    Probable fix: add a type signature that fixes these type variable(s) 
    In the second argument of `(.)', namely `read' 
    In the second argument of `(.)', namely `flatten . read' 
    In the expression: print . flatten . read 

有人能幫助我瞭解如何/爲什麼有型歧義和我怎樣才能使代碼明確。

+0

您認爲GHC應該找到什麼類型? – misterbee

回答

7

只要類型變量由於函數應用而消失,Haskell中就會出現模糊類型。你在這裏的那個,read/show是常見的。這裏的問題:

讓我們嘗試讀取一個字符串,這個操作有型

read :: Read a => String -> a 

這樣,如果我們給它一個字符串,我們只得到一個類型,看起來像

read "()" :: Read a => a 

換句話說,類型系統還沒有能夠選擇具體的類型---它只是知道,無論答案是什麼,它必須是Read able。

的問題是,如果我們回頭周圍並出示馬上我們將函數

show :: Show a => a -> String 

這也不能完全指定類型a。結合他們給了我們

show (read "()") :: String 

,我們已經失去了所有的機會在什麼中間型應該已經決定。

由於這種歧義,Haskell不允許使用這種表達式。你通過插入一個完全限制類型的函數來修復它。一種常見的方法是使用功能asTypeOf

asTypeOf :: a -> a -> a 
asTypeOf = const 

,其確保所述第一和第二參數具有相同的類型。

> show (read "()" `asTypeOf`()) :: String 
"()" 

在您的特定例子中,你需要確定什麼aNestedList a。一個簡單的方法是明確地將flatten作爲具體類型。

print . (flatten :: NestedList Int -> [Int]) . read $ concat args 
+1

感謝您的精彩解釋!我刪除了類型歧義,並按預期運行! –

+0

很高興能幫到你! –

4

這是一個經典的問題。類型類的「ad hoc」多態性使得類型推斷不完整,並且你剛剛被咬傷。讓我們看看這些作品。對於

Read a => Read (NestedList a) 
Show a => Show (NestedList a) 
Read a => Read [a] 
Show a => Show [a] 

read :: Read x => String -> x 
flatten :: NestedList a -> [a] 
print :: Show y => y -> IO() 

,我們還會有機器產生的情況下,現在讓我們來解決我們得到的公式,當我們嘗試構建組成。

print .  flatten    . read 

     y = [a]   NestedList a = x 

這意味着我們需要

Show [a]      Read (NestedList a) 

,從而

Show a      Read a 

,我們已經使用了我們的所有信息,而不確定a,因此相關ReadShow實例。

正如J. Abrahamson已經建議的那樣,您需要做一些決定a的事情。有很多方法可以做到這一點。我傾向於傾向於使用類型註釋來編寫奇特的術語,其唯一目的是使類型更加明顯。我第二個建議給組合中的一個組件提供一個類型,但是我可能選擇(read :: String -> NestedList Int),因爲這是引入含糊不清的類型的操作的操作。

+0

我以前很喜歡'asTypeOf',直到我開始發現自己在寫'(\'asTypeOf \'(undefined :: Etc))''。就像這樣,爲了使它工作,我必須將我的示例從'read'3「'更改爲'read」()「'。所有說,類型註釋是老闆。 –