Francesco, 

thanks, that was very enlightening.  That concatenated functions should have matching inputs / outputs is obvious of course, but I just didn't think of that. Duh!
That IO () can't be converted to String is probably just as obvious, but it wasn't for me.

For hello_pure I tried this:

hello_pure :: Int -> String
hello_pure n
| n < 1 = ""
| otherwise = "Hello World" ++ "\n" ++ hello_pure ( n - 1 )

And it works, although
++ "\n" ++
doesn't feel so elegant.