用ghc中的`reads`模糊错误

我正在测试使用GHC-7.8.2在48小时内编写自己的计划的代码,这给我一个关于模糊的错误,我不记得在以前版本的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 。 有人可以解释什么引发了错误?

- 编辑 -

只是一些后续行动。 根据我的理解,函数类型unpackNum :: LispVal -> IntegerunpackNum :: LispVal -> Integer fst $ parsed !! 0的事实fst $ parsed !! 0 fst $ parsed !! 0是它的返回值,告诉parsed有类型[(Integer,b)] ,并且type ReadS a = String -> [(a,String)]parsed应该是[(a, String)] 。 这两种类型不应统一为[(Integer, String)]并修正parsed类型?

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

- 编辑2 -

从答案中,我可以理解NoMonomorphismRestriction如何在这里引起问题。 不过,我不明白的是,这种“两种类型的表达”行为与Haskell中的懒惰一致。 在parsedreads n的示例reads n在一个块中是相同的表达式,并且应该只评估一次。 它怎么能有型a评估,第一时间Integer第二次?

谢谢,


类型应该统一,但不要存在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

案例与空值的区别

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

null :: [a] -> Bool

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


如果NoMonomorphismRestriction处于活动状态,则触发此操作; 顺便说一句,自7.8以来,GHCi默认情况下是这种情况(参见发行说明,第1.5.2.3节)。

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

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

然后在第一次使用null parsed没有足够的上下文信息解决一下a是。

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

最好的解决方案仍然是使用acomar的答案中提出的模式匹配。

链接地址: http://www.djcxy.com/p/43385.html

上一篇: ambiguity error with `reads` in ghc

下一篇: why polymorphism is so costly in haskell(GHC)?