Xil*_*xio 3 polymorphism haskell
背景
我正在尝试为数值创建一个Parsec解析器.这些值可以是Integer或Double.它们也可以是签名或未签名的.我创建了一个符号解析器,它返回idfor +和negatefor的多态函数-.当给定多态符号函数时,我还有一个构造正确版本的表达式节点的函数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
Run Code Online (Sandbox Code Playgroud)
当我使用上面的代码时,我收到编译错误"无法推断(a~Integer)...".
当我通过let在我的monad中的语句中定义符号函数来更改我的代码时,一切都编译得很好:
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
Run Code Online (Sandbox Code Playgroud)
我的猜测是,在第一种情况下,多态性类型以sign某种方式丢失并转换为Integer -> Integer.
题
sign :: Num a => a -> amonad变量不能作为第一个参数pReal :: (forall a. Num a => a -> a) -> ...,而它的相同类型(sign')的重新定义版本有效?sign在我的monad中创建传递多态变量来实现pReal而不在我的monad中重新定义(sign')它?笔记
我已经尝试过类似明确定义的类型的方法sign有
pNum = pSign >>= \(sign :: Num a => a -> a) -> do ...
Run Code Online (Sandbox Code Playgroud)
或与正常功能定义等相同
请注意,我知道只需返回一个布尔变量sign而不是多态函数,我就可以简化代码.这个问题的关键是要了解类型在这里是如何工作的.
你写
pSign :: Num a => MyParser (a -> a)
Run Code Online (Sandbox Code Playgroud)
这意味着这pSign是一个多态值,对于任何给定的实例化a,它产生一个包含单态函数的解析器.相反,您需要一个包含多态函数的单态解析器,因此:
pSign :: MyParser (forall a. Num a => a -> a)
Run Code Online (Sandbox Code Playgroud)
您还需要进行一些其他更改,以使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
Run Code Online (Sandbox Code Playgroud)