2014-04-04 132 views
4

我正在嘗試使用Parsec庫來解析Token值的列表。我想使用Text.Parsec.Prim中的token函數來匹配單個值。看起來這應該工作:非類型變量約束

type TokenParser a = Parsec [Token]() a 

mytoken :: (Token -> Bool) -> TokenParser Token 
mytoken test = token showTok posFromTok testTok 
    where -- and so on 

這給出了一個編譯錯誤:

No instance for (Stream [Token] Identity Token) 
    arising from a use of `Prim.token' 
Possible fix: 
    add an instance declaration for (Stream [Token] Identity Token) 

好吧,讓我們改變對mytoken:

mytoken :: Stream [Token] Identity Token => (Token -> Bool) -> TokenParser Token 

這工作,之後我們的類型聲明添加{-# LANGUAGE FlexibleContexts #-}擴展名。

這是怎麼回事?首先,Text.Parsec.Prim中的Stream類定義具有Monad m => Stream [tok] m tok作爲實例之一。 Stream [Token] Identity Token是否已經被該實例覆蓋?其次,這是甚麼限制什麼?在mytoken的類型中沒有類型變量可以被約束。

更糟糕的是,當我在另一個函數中使用新的「約束」mytoken時,我得到的錯誤完全相同,它實際上需要在函數的類型上放置相同的,看似無操作類型的約束試圖撥打mytoken

如果任何人都可以幫我解釋一下這種類型約束是幹什麼的,我會非常感激。

回答

9

以下文件是我可以構建的最小文件,它可以重現您的問題。 (在未來,你應該爲我們創造這樣一個自己的文件。)

import Text.Parsec.Prim 

data Token 

type TokenParser a = Parsec [Token]() a 

mytoken :: (Token -> Bool) -> TokenParser Token 
mytoken test = token showTok posFromTok testTok 

showTok = undefined 
posFromTok = undefined 
testTok = undefined 

Text.Parsec.PrimText.Parsec修正錯誤更改進口。 (適當的實例在Text.Parsec.String定義,這是Text.Parsec.Prim進口的。)你也問,爲什麼這種變化使得它編譯,但不工作:

mytoken :: Stream [Token] Identity Token => (Token -> Bool) -> TokenParser Token 

一般的規則是,輸入「C => T」表示:「你可以使用這個東西,就好像它是一個T,只要你能證明約束C滿足」。所以給mytoken你所說的類型說要等到mytoken被用作一個普通的舊的(Token -> Bool) -> TokenParser Token,並且只要它一出現,就盡力履行顯示Stream [Token] Identity Token成立的義務。由於您仍未輸入適當的實例,因此無法履行該義務並投訴。

+0

太棒了!這工作。將來我會確保包含一個最低限度複製的文件。 在沒有導入適當的實例的情況下,您是否有任何洞察類型約束正在做什麼? – Cardano

+1

@Cardano是的,當然。我會添加一個關於這個的句子。 –

+2

請注意,最近這個實例已經[deorphanized](https://github.com/aslatter/parsec/commit/ec7d75098740d9315736f2464f09b699bf37f436),所以從下一個版本開始這不應該是個問題。 –