There are three errors in your code. Copying it and starting it in GHC gives the following messages:
temp.hs:9:13
Couldn't match expected type `Char' with actual type `Int'
Expected type: String
Actual type: [Int]
In the first argument of `putStrLn', namely `xs'
In the expression: putStrLn xs
This one is quite clear. putStrLn needs a String, but xs is a list of Ints. So simply using print xs instead of putStrLn xs solves the problem (print = putStrLn . show).
The next two are actually about the the same problem:
temp.hs:13:38:
No instance for (Monad ((->) t0))
arising from a use of `return'
Possible fix: add an instance declaration for (Monad ((->) t0))
In the expression: return loop (n - 1)
In the expression:
if 0 == n then return () else return loop (n - 1)
In an equation for `loop':
loop n = if 0 == n then return () else return loop (n - 1)
temp.hs:13:45:
Couldn't match expected type `IO ()'
with actual type `Int -> IO ()'
In the first argument of `return', namely `loop'
In the expression: return loop (n - 1)
In the expression:
if 0 == n then return () else return loop (n - 1)
The problem is in the types. loop is of type Int -> IO (). So the first branch of the function is alright, because you return (). However, in the else branch, you return something completely different, because return is not some built-in statement of the language, but a normal function. So return loop (n - 1) first lifts your loop function into the monad and than applies it to (n - 1).
Instead, what you want is:
loop n = if n == 0 then return () else loop (n - 1)
Also note that you don't need 0 == n in Haskell, as there is no way to accidentally use assignment instead of equality comparison (it doesn't compile).
Edit: As the other answers pointed out, loop isn't really doing anything - it only calls itself n-1 times and then returns ().