I have the following Haskell code:
-- Problem 69
import ProjectEuler
phi :: Integer -> Integer
phi n = n * product [p - 1 | p <- primeDivisors n] `div` product [p | p <- primeDivisors n]
-- primeDivisors n is a list of the prime divisors of n
maxRatio :: (Int, Int, Double) -> (Int, Int, Double) -> (Int, Int, Double)
maxRatio t1@(_, _, x) t2@(_, _, y)
| x > y = t1
| otherwise = t2
main = print (foldl
maxRatio
(0, 0, 0.0)
[(n, phi n, ratio) | n <- [2..max], let ratio = fromIntegral n / (fromIntegral (phi n))]
)
where max = 1000
which gives the following error:
Couldn't match expected type `Int' with actual type `Integer'
In the expression: n
In the expression: (n, phi n, ratio)
In the third argument of `foldl', namely
`[(n, phi n, ratio) |
n <- [2 .. max],
let ratio = fromIntegral n / (fromIntegral (phi n))]'
I suspect that in the triple (0, 0, 0.0) the 0’s are type Int. Is 0 always type Int or is ghci deducing the type as Int in this case? If the later, how do I force it to be type Integer instead? Or is there something else that causes this error?
Haskell can generally infer the type of numeric literals such as
0as whatever appropriate type you need them to be. This is because it knows what functions you pass them to; if I have a functionphi :: Integer -> Integer, and I callphi 0, Haskell knows that that particular0must have been anInteger. It’s also fine if I call a functionpho :: Int -> Intwithpho 0; that particular0is inferred to be anInt.However
IntandIntegerare different types, and there’s no way one particular0can be passed to bothphiandpho.Your issue is simply that the tuples that
maxRatiodeals with are typed (by you)(Int, Int, Double), but that one such tuple is constructed as(n, phi n, ratio). Sincephitakes and returnsInteger, thenin that expression has to be anInteger. But then that doesn’t work formaxRatio, so you get the error.Depending on which type you actually wanted (
IntorInteger), all you need to do is change the type signature ofphiormaxRatioso that they’re working with the same kind of number. Haskell will decide that your literally written0s are whatever numeric type is necessary to make that work, provided there is one that can make it work!Note that the error messaged specifically told you that it was
nin(n, phi n, ratio)that was expected to be anIntand was actually anInteger. The(0, 0, 0.0)tuple is never mentioned. Often type errors originate somewhere other than where the compiler points you (since all the compiler can do is spot that different chains of inference produce inconsistent requirements on the type of something, with no way to know which part of the whole process is “wrong”), but in this case it did pretty well.Haskell gets a (fairly justified) bad rep for inscrutable error messages, but it can help a lot to start from what the compiler is telling you is the problem and try to figure out why the facts it’s complaining about arise from your code. This will be painful at first, but you’ll quickly develop a basic literacy in Haskell’s error messages (at least the more straightforward ones) that will help you spot these kinds of errors really quickly, which makes the compiler a very powerful error-detection system for you.