将函数分配给monad中的变量时丢失多态性

时间:2014-01-13 02:32:50

标签: haskell polymorphism

背景

我正在尝试为数值创建Parsec解析器。这些值可以是IntegerDouble。它们也可以是签名或未签名的。我创建了一个符号解析器,它为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')有效?
  • 如何在我的monad中创建的多态变量sign传递给pReal函数而不在我的monad中重新定义(sign')?

备注

我已经尝试过使用

明确定义sign类型的方法
pNum = pSign >>= \(sign :: Num a => a -> a) -> do ...

或与正常功能定义等相同。

请注意,我知道只需从sign返回一个布尔变量而不是多态函数,我就可以简化代码。这个问题的关键是要了解类型在这里是如何工作的。

1 个答案:

答案 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