ghc-7.8中“读取”的模糊性错误

时间:2014-04-24 22:22:44

标签: haskell ghc

我正在使用GHC-7.8.2测试Write yourself a Scheme in 48 hours的代码,这给我一个关于模糊性的错误,我不记得在之前版本的GHC中遇到过这种错误。 摘录如下,问题行标记为:

data LispVal = Atom String
             | List [LispVal]
             | DottedList [LispVal] LispVal
             | Number Integer
             | String String
             | Bool Bool
unpackNum :: LispVal -> Integer
unpackNum (Number n) = n
unpackNum (String n) = let parsed = reads n in  --problem line 
                          if null parsed 
                            then 0
                            else fst $ parsed !! 0
unpackNum (List [n]) = unpackNum n
unpackNum _ = 0

,错误说:

No instance for (Read a0) arising from a use of ¡®parsed¡¯
The type variable ¡®a0¡¯ is ambiguous
Note: there are several potential instances:
  instance Read a => Read (Control.Applicative.ZipList a)
    -- Defined in ¡®Control.Applicative¡¯
  instance Read () -- Defined in ¡®GHC.Read¡¯
  instance (Read a, Read b) => Read (a, b) -- Defined in ¡®GHC.Read¡¯
  ...plus 26 others

如果我将问题行更改为

unpackNum (String n) = let parsed = reads n ::[(Integer,String)] in 

然后一切正常。

我不明白为什么GHC无法从unpackNum的签名推断出ReadS的类型。有人可以解释触发错误的原因吗?

- 编辑 -

只是一些跟进。根据我的理解,函数类型unpackNum :: LispVal -> Integer以及fst $ parsed !! 0是其返回值的事实表明parsed具有类型[(Integer,b)],并且来自type ReadS a = String -> [(a,String)]parsed应为[(a, String)]。这两种类型不应该统一到[(Integer, String)]并修复parsed的类型吗?

有人可以解释为什么NoMonomorphismRestriction会打破上述推理?

- EDIT2 -

从答案中,我可以理解NoMonomorphismRestriction如何在这里引起问题。但是,我不明白的事实是,这种“两种类型的相同表达”行为如何与Haskell中的懒惰一致。在示例parsedreads n中,一个块中的表达式相同,应仅评估一次。第一次评估时如何a,第二次Integer

谢谢,

2 个答案:

答案 0 :(得分:6)

如果NoMonomorphismRestriction处于有效状态,则会触发此操作;自从7.8 (see release notes, Section 1.5.2.3)以来,GHTi默认情况下是btw。

如果禁用单态限制,parsed的定义将获得多态类型,即

parsed :: Read a => [(a, String)]

然后null parsed中的第一次使用没有足够的上下文信息来解析a的内容。

这恰好是少数情况下单态性限制实际上有所好处的少数情况之一。因为具有多态类型,即使两个使用站点都有足够的类型 解决类约束的信息,实际解析会发生两次。

最好的解决方案仍然是按照acomar的回答中的建议使用模式匹配。

答案 1 :(得分:5)

类型应统一但不存在NoMonomorphismRestriction(如@FedorGogolev和@kosmikus的评论中所述)。但是,以下更惯用的方法在任何情况下都不需要类型注释:

data LispVal = Atom String
             | List [LispVal]
             | DottedList [LispVal] LispVal
             | Number Integer
             | String String
             | Bool Bool
unpackNum :: LispVal -> Integer
unpackNum (Number n) = n
unpackNum (String n) = case reads n of
                           [] -> 0
                           ((x, _):xs) -> x
unpackNum (List [n]) = unpackNum n
unpackNum _ = 0

Case和Null之间的差异

归结为null是一个函数,而case是直接的语法。

null :: [a] -> Bool

因此,在启用了-XNoMonomorphismRestriction的情况下,在提供参数时,这将保持多态。该函数不以任何方式限制参数类型,因此编译器无法确定reads的返回类型,从而导致错误。在函数调用的站点,类型是不明确的。对于case语句,编译器具有要使用的整个表达式,因此模式匹配以优化reads的返回类型。