views:

125

answers:

2

Here is a sample program from RWH book. I'm wondering why the first works great but the second can't even compile? The only difference is the first one uses 2 tabs after where mainWith func = do whereas the second uses only 1. Not sure what difference does that mean? Why the second fails to compile? And also why do construct can be empty?

Thanks a lot, Alex

-- Real World Haskell Sample Code Chapter 4:
-- http://book.realworldhaskell.org/read/functional-programming.html

import System.Environment (getArgs)

interactWith func input output = do
    s <- readFile input
    writeFile output (func s)

main = mainWith myFunction
    where mainWith func = do
            args <- getArgs
            case args of 
                [fin, fout] -> do
                    interactWith func fin fout
                _ -> putStrLn "error: exactly two arguments needed"

myFunction = id


-- The following code has a compilation error
-- % ghc --make interactWith.hs
-- [1 of 1] Compiling Main             ( interactWith.hs, interactWith.o )
-- 
-- interactWith.hs:8:26: Empty 'do' construct


import System.Environment (getArgs)

interactWith func input output = do
    s <- readFile input
    writeFile output (func s)

main = mainWith myFunction
    where mainWith func = do
        args <- getArgs
        case args of 
            [fin, fout] -> do
                interactWith func fin fout
            _ -> putStrLn "error: exactly two arguments needed"

myFunction = id
A: 

The do-block can not be empty, that's why you get the error. When using only one tab args <- getArgs is seen as part of the where-block, not the do-block, so the do-block is empty and you get an error.

The thing is that unless you use {} and ; to explicitly state which block goes from where to where, haskell relies on indendation. And since you indented your line only by one level, it was seen as part of the where-block.

sepp2k
+4  A: 

The definition of the mainWith function is indented to column 10:

    where mainWith func = do
          ^

The contents of the do block started in this line are only indented to column 8:

        args <- getArgs
        case args of 
            ...
        ^

If you increase the indentation of the contents of the do block to be also indented at least to column 10, the code is parsed correctly. With the current indentation the lines that should belong to the do block are seen to be part of the where clause, but not the mainWith function.

sth
ah, wow, that's mysterious. Thanks a heap, that explains. Haven't expected the indent starts after 'where'.
Alex Dong
@Alex: That's because the `where` has it's own indent level (starting as expected at the `w`). You could define several function in a single `where` block. The function definition self starts it's own indent level, separate of the one introduced by `where`.
sth