2014-01-13 24 views
3

背景失去多態性,而單子

內部分配功能給一個變量我試圖創建數值的秒差距解析器。這些值可以是IntegerDouble。它們也可以是有符號或無符號的。我創建了一個符號解析器,它爲+negate返回-的多態函數id。當我給出多態符號函數和Either Integer Double時,我也有一個函數構造我的表達式節點的正確版本。我試圖把它放在一起,如下面簡化版本的代碼所示。

{-# LANGUAGE RankNTypes #-} 

-- (...) 

data Expr = IntExpr Integer | DoubleExpr Double 
pSign :: Num a => MyParser (a -> a) -- returns id for + or negate for - 
pReal :: (forall a. Num a => a -> a) -> Either Integer Double -> MyParser Expr 

pNum :: MyParser Expr 
pNum = do 
    sign <- pSign 
    numVal <- ParsecToken.naturalOrFloat lexer 
    pReal sign numVal 

當我使用上面的代碼時,出現編譯錯誤「Could not deduce(a〜Integer)...」。

當我定義我的單子一個let語句內簽到功能改變我的代碼,一切編譯罰款:

pNum :: MyParser Expr 
pNum = do 
    sign <- pSign 
    numVal <- ParsecToken.naturalOrFloat lexer 
    let t = sign 1 
     sign' :: Num a => a -> a 
     sign' = if t == 1 then id else negate 
    pReal sign numVal 

我的猜測是,在第一種情況下,polymophic型sign被莫名其妙地丟失,轉換到Integer -> Integer

問題

  • 什麼機制使原來sign :: Num a => a -> a單子變量不爲pReal :: (forall a. Num a => a -> a) -> ...第一個參數工作,而其重新定義了同類型的版本(sign')的作品?
  • 如何在我的monad中實現將在我的monad中創建的多態變量sign傳遞給pReal函數,而無需重新定義它(sign')?

注意

我已經嘗試過的方法一樣的sign明確定義類型與

pNum = pSign >>= \(sign :: Num a => a -> a) -> do ... 

或具有正常功能的定義相同等

請注意,我知道,我可以簡化代碼,只需返回一個來自sign的布爾變量而不是多態函數灰。這個問題的關鍵在於瞭解類型如何在這裏工作。

+2

在未來,這是禮貌,包括一個完整的,可編譯例子 - 打樁了一些無關緊要的事情,包括必要的進口,等等'undefined'或類似的是好的,只是,只要我能放棄它在我的文本編輯器,並立即開始玩它。 –

+0

@DanielWagner:謝謝你的提示。我並沒有通過'undefined'來描述,所以我包含了沒有定義的不可編譯的版本。 – Xilexio

回答

9

你寫

pSign :: Num a => MyParser (a -> a) 

這意味着pSign是一個多態值,對於的a任何給定的實例,產生含有一個單形性函數的分析器。相反,你要包含多態功能的單態分析器,所以:

pSign :: MyParser (forall a. Num a => a -> a) 

你需要做一些其他的變化,以及獲得GHC瞭解如何讓事情變得多態,直到最後一秒。這是一個完整的,可編輯的例子。

{-# LANGUAGE ImpredicativeTypes, LiberalTypeSynonyms, RankNTypes, ScopedTypeVariables #-} 

import Text.ParserCombinators.Parsec 

type MyParser = Parser 

data Expr = IntExpr Integer | DoubleExpr Double 
pSign :: MyParser (forall a. Num a => a -> a) -- returns id for + or negate for - 
pReal :: (forall a. Num a => a -> a) -> Either Integer Double -> MyParser Expr 
foo :: MyParser (Either Integer Double) 
pSign = undefined 
pReal = undefined 
foo = undefined 

pNum :: MyParser Expr 
pNum = 
    pSign >>= \(sign :: forall a. Num a => a -> a) -> 
    foo >>= \numVal -> 
    pReal sign numVal 
+4

此外,在這裏使用新類型可能很有用: 'newtype NumOp = NumOp(Num a => a - > a)''pSign :: MyParser NumOp' 'pReal :: NumOp - > Either Integer Double - > MyParser Expr' 'pNum = pSign >> = \(NumOp sign) - > ...' - 少打字,然後不需要ImpandicativeTypes! – aemxdp