Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 Integrals? 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
like image 876
8128 Avatar asked Jan 19 '23 18:01

8128


1 Answers

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.

like image 150
Josh Lee Avatar answered Feb 01 '23 09:02

Josh Lee