Why does this Haskell complain about ambigous types when its extended?
The following returns True (because 2147483647 is a prime).
length [f | f <- [2..(floor(sqrt 2147483647))], 2147483647 `mod` f == 0 ] == 0
Why doesn't it work when I try to extend it as below?
Prelude> [n | n <- [2..], length [f | f <- [2..(floor(sqrt n))], n `mod` f == 0 ] == 0 ]
<interactive>:1:39:
Ambiguous type variable `t' in the constraints:
`RealFrac t' arising from a use of `floor' at <interactive>:1:39-51
`Integral t' arising from a use of `mod' at <interactive>:1:56-64
`Floating t' arising from a use of `sqrt' at <interactive>:1:45-50
Probable fix: add a type signature that fixes these type variable(s)
I don't understand though, why is a RealFrac arising from a use of floor? I thought floor took RealFracs and produced Integral开发者_Python百科s? Plus it didn't complain with the above example, I'm only inputting more integers as I did then.
Prelude> :t floor
floor :: (RealFrac a, Integral b) => a -> b
Let’s un-obfuscate this slightly:
Prelude> (\x -> x `mod` (floor . sqrt) x) 2
<interactive>:1:24:
Ambiguous type variable `b' in the constraints:
`Floating b' arising from a use of `sqrt' at <interactive>:1:24-27
`Integral b' arising from a use of `mod' at <interactive>:1:7-30
`RealFrac b' arising from a use of `floor' at <interactive>:1:16-20
Probable fix: add a type signature that fixes these type variable(s)
You’re using the value of n
as a float, passing it to sqrt
and floor
. You’re then using that result as an int, passing that result to mod
. The compiler can’t name a type with all those instances.
The reason it works in your first example, in other words
Prelude> 2 `mod` (floor . sqrt) 2
0
is because you’re using two different numeric literals. One can be an int and one can be a float. If you’re using the same value for both, you need to call fromIntegral
to convert the int to a float.
You can get a different error message by adding a type signature, changing [2..]
to [2..] :: [Integer]
:
No instance for (RealFrac Integer)
arising from a use of `floor' at <interactive>:1:52-64
No instance for (Floating Integer)
arising from a use of `sqrt' at <interactive>:1:58-63
This might make it more clear that you’re using the value of n
as two different types.
As pointed out by C. A. McCann below, my answer is not correct :-)
As far as I can see, it's because the list you produce can consist of any instance of Floating
since the type signature of sqrt
is
sqrt :: Floating a => a -> a
By precomposing sqrt
with fromIntegral :: (Integral a, Num b) => a -> b
, you get the desired result:
Prelude> take 10 $ [n | n <- [2..], length [f | f <- [2..(floor(sqrt (fromIntegral n)))], n `mod` f == 0 ] == 0 ]
[2,3,5,7,11,13,17,19,23,29]
精彩评论