开发者

Why does this Haskell complain about ambigous types when its extended?

开发者 https://www.devze.com 2023-04-04 03:11 出处:网络
The following returns True (because 2147483647 is a prime). length [f | f <- [2..(floor(sqrt 2147483647))], 2147483647 `mod` f == 0 ] == 0

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]
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号