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 "Hello World")
In this case we have n
repetitions of the string "Hello World"
, 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 -> IO ()
hello_worlds n = putStrLn $ unlines (replicate n "Hello World")
main :: IO ()
main = do
n <- readLn
hello_worlds n
The last small change I'd make is to rename hello_worlds
to helloWorlds
. Haskell style prefers CamelCase.