Reputation: 952
I wrote a Haskell function that calculates the factorial of every number in a given list and prints it to the screen.
factPrint list =
if null list
then putStrLn ""
else do putStrLn ((show.fact.head) list)
factPrint (tail list)
The function works, but I find the third line a bit confusing. Why hasn't the compiler(GHC) reported an error on it since there is no "do" before the "putStrLn" (quasi?)function? If I omit "do" from the 4th line, an error pops up as expected.
I'm quite new to Haskell and its ways, so please pardon me if I said something overly silly.
Upvotes: 4
Views: 429
Reputation: 18389
If you are new to Haskell, think of do
as similar to required braces after if
in a C-like language:
if (condition)
printf("a"); // braces not required
else {
printf("b"); // braces required
finish();
}
do
works the same way in Haskell.
Maybe it would help to look at the type of factPrint, and then refactor to use pattern matching:
factPrint :: [Int] -> IO ()
factPrint [] = putStrLn ""
factPrint list = do
putStrLn (show.fact.head) list
factPrint (tail list)
So, if factPrint returns IO ()
, and the type of putStrLn ""
is IO ()
, then it's perfectly legal for factPrint []
to equal putStrLn ""
. No do
required--in fact, you could just say factPrint [] = return ()
if you didn't want the trailing newline.
Upvotes: 5
Reputation: 28655
The do keyword is used for sequencing, an if-then-else in Haskell doesn't have to contain a do
at all if each branch is a single statement eg.
if a
then b
else c
You need the do
in your example as you are sequencing two operations on your else branch. If you omit the do
then the factPrint(tail list)
statement is considered to not be part of the function and thus the compiler complains as it's encountered an unexpected statement.
Upvotes: 3
Reputation: 370415
do
is used to tie multiple monadic expressions together. It has no effect when followed by only a single expression.
For the if to be well-formed it is only necessary that the then-clause and the else-clause have the same type. Since both clauses have the type IO ()
this is the case.
Upvotes: 4
Reputation: 122499
do putStrLn ((show.fact.head) list)
factPrint (tail list)
is actually another way of writing
putStrLn ((show.fact.head) list) >> factPrint (tail list)
which, in turn, means
putStrLn ((show.fact.head) list) >>= \_ -> factPrint (tail list)
The do
notation is a convenient way of stringing these monads together, without this other ugly syntax.
If you only have one statement inside the do
, then you are not stringing anything together, and the do
is redundant.
Upvotes: 11