![](/img/trans.png)
[英]can't build lambdabot with ghc-7.8 (@ haskell-src-exts)
[英]ambiguity error with `reads` in ghc-7.8
我正在测试使用GHC-7.8.2 在48小时内为自己编写一个Scheme的代码,这给我一个关于模糊性的错误,我不记得在以前版本的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 -> Integer
以及unpackNum :: 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
会打破上述推理吗?
- EDIT2 -
从答案中,我可以理解NoMonomorphismRestriction
如何在这里引起问题。 但是,我不明白的事实是,这种“两种类型的相同表达”行为如何与Haskell中的懒惰一致。 在parsed
或reads n
的示例reads n
是一个块中的相同表达式,应仅评估一次。 它怎么能有型a
评估,第一时间Integer
第二次?
)
谢谢,
如果NoMonomorphismRestriction
处于活动状态,则会触发此操作; 其中,顺便说一下,自7.8以来GHCi默认情况就是这样(见发行说明,第1.5.2.3节) 。
如果禁用单态限制,则parsed
的定义将获得多态类型,即
parsed :: Read a => [(a, String)]
然后在null parsed
的第一次使用没有足够的上下文信息来解析a
是什么。
这恰好是单形态限制实际上有所好处的少数情况之一。 因为对于多态类型,即使两个使用站点都有足够的类型信息来解析类约束,实际的解析也会发生两次。
最好的解决方案仍然是使用acomar的答案中建议的模式匹配。
这些类型应该统一,但不存在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
的返回类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.