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

tin*_*lyx 3 haskell ghc

我正在测试使用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
Run Code Online (Sandbox Code Playgroud)

,错误说:

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
Run Code Online (Sandbox Code Playgroud)

如果我将问题行更改为

unpackNum (String n) = let parsed = reads n ::[(Integer,String)] in 
Run Code Online (Sandbox Code Playgroud)

一切正常.

我不明白为什么GHC无法从签名中推断出ReadS的类型unpackNum.有人可以解释是什么引发了错误?

(

- 编辑 -

只是一些后续行动.据我所知,功能类型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中的懒惰一致.在示例中,parsed或者reads n在一个块中是相同的表达式,应该只评估一次.如何a在第一次评估和Integer第二次进行打字?

)

谢谢,

kos*_*kus 6

如果NoMonomorphismRestriction处于活动状态则触发; 其中,顺便说一下,自7.8以来GHCi默认情况就是这样(见发行说明,第1.5.2.3节).

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

parsed :: Read a => [(a, String)]
Run Code Online (Sandbox Code Playgroud)

然后第一次使用null parsed没有足够的上下文信息来解决问题a.

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

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


aco*_*mar 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
Run Code Online (Sandbox Code Playgroud)

案例与空间的区别

它归结为这null是一个函数而case不是直接语法的事实.

null :: [a] -> Bool
Run Code Online (Sandbox Code Playgroud)

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

  • 感谢您的投入.我不知道为什么要downvote.我确实有一个问题.(纠正我,如果我错了)`类型ReadS a = String - > [(a,String)]`,所以该对的`snd`被称为`String`,不是吗? (5认同)
  • 好吧,所以这个问题的真正答案是:如果禁用单态限制,`parsed`的定义会得到一个多态类型,即`Read a => [(a,String)]`,然后第一次使用`null parsed`没有足够的上下文信息来解决`a`是什么.(注意刚刚发生的事情:这实际上是*是一个单形态限制在某种程度上可取的例子.这些情况很少见,但它们会发生......) (3认同)