Haskell can usually infer the type of numeric literals, such as 0 , like any suitable type that you need. This is because he knows with what functions you transfer them; if I have a function phi :: Integer -> Integer and I call phi 0 , Haskell knows that this 0 must be Integer . It is also fine if I call the function pho :: Int -> Int with pho 0 ; that particular 0 is defined as Int .
However, Int and Integer are different types, and no concrete 0 can be passed for both phi and pho .
Your problem is simply that the sets that maxRatio deals with are typed (by you) (Int, Int, Double) , but one such tuple is built like (n, phi n, ratio) . Since phi accepts and returns Integer , n in this expression must be Integer . But then this does not work for maxRatio , so you get an error.
Depending on what type you really wanted ( Int or Integer ), all you have to do is change the signature like phi or maxRatio so that they work with the same number, Haskell will decide that your literally written 0 is this is any digital type needed to do this job, provided it can make it work!
Note that the error message reported that it was n in (n, phi n, ratio) , which was supposed to be Int and was actually Integer . The tuple (0, 0, 0.0) never mentioned. Often type errors occur somewhere other than where the compiler tells you (since all the compiler can do is that different output chains cause conflicting requirements for the type of something, without being able to find out which part of the whole process is "wrong",), but in this case it was pretty good.
Haskell gains (rightly justified) a bad reputation for infallible error messages, but it can help a lot to start with what the compiler tells you and try to find out why the facts are complaining about your code. It will be painful at first, but you will quickly develop basic literacy in Haskell error messages (at least simpler ones) that will help you quickly detect these errors, which makes the compiler very powerful error detection for you.