ambiguity error with `reads` in ghc
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. 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
. 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
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)]
. Shouldn't these two types unify to [(Integer, String)]
and fix the type for parsed
?
Can someone please explain why NoMonomorphismRestriction
would break the above reasoning?
-- EDIT2 --
From the answers, I can understand how NoMonomorphismRestriction
could cause the issue here. Still, what I don't understand is the fact that how this "two type for the same expression" behavior consistent with laziness in Haskell. In the example parsed
or reads n
is the same expression in one block and should be evaluated only once. How can it have type a
the first time of evaluation and Integer
the second time?
)
Thanks,
The types should unify but don't in the presence of the NoMonomorphismRestriction
(as noted in the comments by @FedorGogolev and @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 :: [a] -> Bool
So with -XNoMonomorphismRestriction enabled, this is left as polymorphic as possible when the argument is supplied. 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. 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
.
This is triggered if NoMonomorphismRestriction
is active; which, btw, is now the case by default in GHCi since 7.8 (see release notes, Section 1.5.2.3).
If the monomorphism restriction is disabled, the definition of parsed
gets a polymorphic type, namely
parsed :: Read a => [(a, String)]
and then the first use in null parsed
doesn't have sufficient contextual information to resolve what a
is.
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.
链接地址: http://www.djcxy.com/p/43386.html上一篇: 使用GHC只配置一个功能(或成本中心)
下一篇: 用ghc中的`reads`模糊错误