简体   繁体   English

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

[英]ambiguity error with `reads` in ghc-7.8

I am testing the code for Write yourself a Scheme in 48 hours with GHC-7.8.2, which gives me an error about ambiguity that I don't recall encountering in previous versions of GHC. 我正在测试使用GHC-7.8.2 在48小时内自己编写一个Scheme的代码,这给我一个关于模糊性的错误,我不记得在以前版本的GHC中遇到过。 The excerpt is below, with the problem line marked: 摘录如下,问题行标记为:

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

, and the error says: ,错误说:

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

If I change the problem line to 如果我将问题行更改为

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

then everything works fine. 一切正常。

I don't see why GHC failed to infer the type for ReadS from the signature of unpackNum . 我不明白为什么GHC未能推断从签名读取类型unpackNum Can someone please explain what triggered the error? 有人可以解释是什么引发了错误?

(

-- EDIT -- - 编辑 -

Just some follow-up. 只是一些后续行动。 From what I understand, the function type unpackNum :: LispVal -> Integer and the fact that fst $ parsed !! 0 据我所知,函数类型unpackNum :: LispVal -> Integer以及unpackNum :: LispVal -> Integer fst $ parsed !! 0的事实fst $ parsed !! 0 fst $ parsed !! 0 is a return value of it tells that parsed has type [(Integer,b)] , and from type ReadS a = String -> [(a,String)] , the parsed should be [(a, String)] . fst $ parsed !! 0是它的返回值告诉parsed类型为[(Integer,b)] ,并且从type ReadS a = String -> [(a,String)]parsed应该是[(a, String)] Shouldn't these two types unify to [(Integer, String)] and fix the type for parsed ? 这两种类型不应该统一到[(Integer, String)]并修复parsed的类型吗?

Can someone please explain why NoMonomorphismRestriction would break the above reasoning? 有人可以解释为什么NoMonomorphismRestriction会打破上述推理吗?

-- EDIT2 -- - EDIT2 -

From the answers, I can understand how NoMonomorphismRestriction could cause the issue here. 从答案中,我可以理解NoMonomorphismRestriction如何在这里引起问题。 Still, what I don't understand is the fact that how this "two type for the same expression" behavior consistent with laziness in Haskell. 但是,我不明白的事实是,这种“两种类型的相同表达”行为如何与Haskell中的懒惰一致。 In the example parsed or reads n is the same expression in one block and should be evaluated only once. parsedreads n的示例reads n是一个块中的相同表达式,应仅评估一次。 How can it have type a the first time of evaluation and Integer the second time? 它怎么能有型a评估,第一时间Integer第二次?

)

Thanks, 谢谢,

This is triggered if NoMonomorphismRestriction is active; 如果NoMonomorphismRestriction处于活动状态,则会触发此操作; which, btw, is now the case by default in GHCi since 7.8 (see release notes, Section 1.5.2.3) . 其中,顺便说一下,自7.8以来GHCi默认情况就是这样(见发行说明,第1.5.2.3节)

If the monomorphism restriction is disabled, the definition of parsed gets a polymorphic type, namely 如果禁用单态限制,则parsed的定义将获得多态类型,即

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

and then the first use in null parsed doesn't have sufficient contextual information to resolve what a is. 然后在null parsed的第一次使用没有足够的上下文信息来解析a是什么。

This happens to be one of the few cases where the monomorphism restriction actually does some good. 这恰好是单形态限制实际上有所好处的少数情况之一。 Because with the polymorphic type, even if both use sites had sufficient type information to resolve the class constraint, the actual parsing would happen twice. 因为对于多态类型,即使两个使用站点都有足够的类型信息来解析类约束,实际的解析也会发生两次。

The best solution is still to use pattern matching as suggested in acomar's answer. 最好的解决方案仍然是使用acomar的答案中建议的模式匹配。

The types should unify but don't in the presence of the NoMonomorphismRestriction (as noted in the comments by @FedorGogolev and @kosmikus). 这些类型应该统一,但不存在NoMonomorphismRestriction (如@FedorGogolev和@kosmikus的评论中所述)。 However, the following more idiomatic approach removes the need for the type annotation in any case: 但是,以下更惯用的方法在任何情况下都不需要类型注释:

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

The Difference Between Case and Null 案例与空间的区别

It boils down to the fact that null is a function whereas case is straight syntax. 归结为null是一个函数,而case是直接语法。

null :: [a] -> Bool

So with -XNoMonomorphismRestriction enabled, this is left as polymorphic as possible when the argument is supplied. 因此,启用了-XNoMonomorphismRestriction,在提供参数时,这将保持多态。 The function doesn't restrict the argument type in any way, and so the compiler is unable to determine the return type of reads , causing the error. 该函数不以任何方式限制参数类型,因此编译器无法确定reads的返回类型,从而导致错误。 At the site of the function call, the type is ambiguous. 在函数调用的站点,类型是不明确的。 In the case of the case statement, the compiler has the entire expression to work with, and so has the pattern matches to refine the return type of reads . case语句的case ,编译器具有要使用的整个表达式,因此模式匹配以优化reads的返回类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM