背景
我正在尝试为数值创建Parsec解析器。这些值可以是Integer
或Double
。它们也可以是签名或未签名的。我创建了一个符号解析器,它为id
返回多态函数+
,为negate
返回-
。当给定多态符号函数和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
当我使用上面的代码时,我收到编译错误“无法推断(a~Engger)...”。
当我通过在我的monad中的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
我的猜测是,在第一种情况下,多变量类型sign
以某种方式丢失并转换为Integer -> Integer
。
问题
sign :: Num a => a -> a
monad变量不能作为pReal :: (forall a. Num a => a -> a) -> ...
的第一个参数,而其相同类型的重新定义版本(sign'
)有效? sign
传递给pReal
函数而不在我的monad中重新定义(sign'
)?备注
我已经尝试过使用
明确定义sign
类型的方法
pNum = pSign >>= \(sign :: Num a => a -> a) -> do ...
或与正常功能定义等相同。
请注意,我知道只需从sign
返回一个布尔变量而不是多态函数,我就可以简化代码。这个问题的关键是要了解类型在这里是如何工作的。
答案 0 :(得分: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