类型推断细微之处

时间:2014-09-18 00:25:14

标签: haskell parsec

我在理解为什么推断类型签名与我期望的不同时遇到了一些困难。让我们举个例子(我尽量让它尽可能短):

import Control.Applicative
import Data.Word
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Token
import Text.Parsec.Language (emptyDef)
import Text.Parsec.Prim
import Data.Functor.Identity

--parseUInt' :: Num b => ParsecT String u Identity b
parseUInt' = fromInteger <$> decimal (makeTokenParser emptyDef)
--parseUInt1 = fromInteger <$> decimal (makeTokenParser emptyDef)
--parseUInt2 = fromInteger <$> decimal (makeTokenParser emptyDef)

parsePairOfInts = do
    x <- parseUInt'
    char ','
    y <- parseUInt'
    return $ (x, y)

parseLine :: String -> Either ParseError (Word32, Word8)
parseLine = parse parsePairOfInts "(error)"

main = print . show $ parseLine "1,2"

此代码无法编译:

test.hs:21:19:
    Couldn't match type ‘Word32’ with ‘Word8’
    Expected type: Parsec String () (Word32, Word8)
      Actual type: ParsecT String () Identity (Word32, Word32)
    In the first argument of ‘parse’, namely ‘parsePairOfInts’
    In the expression: parse parsePairOfInts "(error)"
Failed, modules loaded: none.

但如果我取消注释parseUInt'的类型签名,它就可以很好地编译。

同时,如果我在GHCi中查询类型信息,它看起来像这样:

λ>:t (fromInteger <$> decimal (makeTokenParser emptyDef))
(fromInteger <$> decimal (makeTokenParser emptyDef))
  :: Num b => ParsecT String u Identity b

但如果我没有明确指定类型签名,那么&#39; b&#39;以某种方式将类型固定为Word32

如果我用两个不同的(但仍然是相同的实现)函数parseUInt'parseUInt1替换parseUInt2,代码也会编译。

我认为如果我没有指定函数的类型,推断类型必须是限制性最小的(Num b =>...),但事实并非如此。

我在这里真正缺少什么?

2 个答案:

答案 0 :(得分:8)

我认为这是可怕的MonomorphismRestriction。如果您没有提供类型签名,那么ghc会尝试推断具体类型签名,如果该函数被实例化为代码中其他地方的具体类型。 ghc看到您使用该函数将Word32解析为parsePairOfInt的第一行,然后在达到{{1}的第二次使用之前将parseUInt'修复为该类型两行下来。这会导致类型错误,因为类型已经实例化为parseUInt',现在类型需要为Word32

答案 1 :(得分:2)

再次看起来像monomorphism restriction。你定义了一些没有&#34;外观&#34;像一个多态值,所以编译器为它推断出一个单态类型。

事实证明这不是你想要的类型,所以你必须通过添加类型签名来明确你想要的多态性。