Print "n" number of "hello world"
Instead of using explicit recursion, you can build your solution out of standard Prelude functions. This frequently ends up being more readable when the functions you're writing are more complex as common patterns are expressed by familiar names instead of properties of your recursive step.
hello_worlds n = putStrLn $ unlines (replicate n &quot;Hello World&quot;)

In this case we have n repetitions of the string &quot;Hello World&quot;, joined by newlines, and printed to stdout.
All top-level function definitions should include type annotations as well. It's a good source of documentation, it serves as a good check that you and the type checker agree on what your function is supposed to be doing, and it often gives the type checker enough information that you don't need to use type annotations inside the body of a function. In this case, by giving a type to hello_worlds you don't need to say what the return value of readLn is, because it can be seen that the n in main must be an Int since it's the argument to hello_worlds.
hello_worlds :: Int -&gt; IO ()
hello_worlds n = putStrLn $ unlines (replicate n &quot;Hello World&quot;)

main :: IO ()
main = do
    n &lt;- readLn
    hello_worlds n

The last small change I'd make is to rename hello_worlds to helloWorlds. Haskell style prefers CamelCase.