0
hello_worlds n =    if n > 0
                    then do putStrLn "Hello World"                            
                            hello_worlds (n-1)              


main = do
   n <- readLn :: IO Int
   hello_worlds n
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555

1 Answers1

3

This is because in Haskell the if-then-else structure, it requires an else. This works:

hello_worlds n = if n > 0
                 then do putStrLn "Hello World"
                         hello_worlds (n-1)
                 else return ()

main = do
   n <- readLn :: IO Int
   hello_worlds n

Where the return () can be seen as a no-operation statement.

This is mainly because I/O in Haskell is done using Monads: The IO a; with a the "return-type" of the Monad. Now evidently you do not return anything, but it could be possible that your I/O monad did (like IO Int), in which case both branches should return something.

Anyway, using an if-then-else block is by some (under some circumstances) considered to be bad as is the do statement as well.


You can replace this if statement (which is by some considered to be un-Haskell); and make it more declarative using:

import Control.Monad(replicateM_)

main = do
   n <- readLn :: IO Int
   replicateM_ n $ putStrLn "Hello World"

because it makes it syntactically very clear that you are going to repeat (replicate) the putStrLn "Hello World" statement n times, whereas for the recursive approach it requires more thinking on how this works.

Community
  • 1
  • 1
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 4
    "do-blocks are bad, avoid them" is a very extreme position. There is a nuanced pedagogical argument about not using do-blocks at first when teaching beginners, but other than that do-blocks are fine and perfectly idiomatic Haskell. As for if-then-else, it is indeed the case that there usually are nicer alternatives, such as the one you show in the second half of the answer. – duplode Sep 21 '15 at 00:38
  • Here we go again... First of all, if you read carefully, it says "are considered by many..."; furthermore it links to a page of the Haskell wiki that explains it more carefully. If I would have to explain all in my course texts, they would be 3k+ pages instead of 300. The statement is a small footnote, it is not relevant to the question itself, but is relevant for people wanting to improve code hygiene and thus read the page behind the link. – Willem Van Onsem Sep 21 '15 at 00:40
  • I noticed that (though I would dispute the "many"). I only mentioned the majority opinion here to hopefully prevent the OP from avoiding do-blocks for no good reason. I wrote it as a comment because I didn't feel it justified a competing answer. – duplode Sep 21 '15 at 00:46
  • @duplode: modified... Happy? – Willem Van Onsem Sep 21 '15 at 00:47
  • 1
    It is certainly better, though I believe "do-blocks are bad" is such a minority stance it doesn't deserve a mention at all (the if-then-else part is fine). Please note that my comments (and, I presume, those of other [haskell] regulars as well) are not meant as nitpicking for the sake of it. We just care about lowering the likelihood of readers getting unnecessarily confused over details. – duplode Sep 21 '15 at 01:09
  • 1
    I don't see how monads have anything to do with it. The reason `if` requires an `else` is that `if` chooses between two expressions. It has to have an expression for the case where the condition holds and one for the case where it does not. – dfeuer Sep 21 '15 at 01:17