Search code examples
haskellif-statementurl-rewritingeofparse-error

Copying lines divisible by 5


It's me again:). I try to write a program which copies lines which number is divisible by 5 to another file. Here is code (sorry for Polish names):

import IO

przepiszConHelper :: Handle -> Handle -> Integer -> Integer -> IO ()
przepiszConHelper wejscie wyjscie liczba licznik = do
    eof <- hIsEOF wejscie
    if eof then return ()
        else
            linia <- hGetLine wejscie
            if (mod licznik liczba) == 0 then
                hPutStrLn wyjscie linia
            przepiszConHelper wejscie wyjscie liczba (licznik + 1)

przepiszCon :: String -> String -> Integer -> IO ()
przepiszCon wejscie wyjscie liczba = do
    wej <- openFile wejscie ReadMode
    wyj <- openFile wyjscie WriteMode
    przepiszConHelper wej wyj liczba 0
    hClose wej
    hClose wyj

main = przepiszCoN "wejscie.txt" "wyjscie.txt" 5

I think it should works... but I get one, strange error:

przepisz.hs:6:9:
    Parse error in pattern: if eof then return () else linia

Which makes no sense for me. I had been using the same expression in another programs and it worked like a harm. I tried to remove these lines and write them with different indentations (I remember that I had some issues with white-spaces before). But I still get the same error:(.


--edit

OK, I have first error... it's just else do instead of else. But here comes another problem:

przepisz.hs:11:25: parse error on input `przepiszConHelper'

Solution

  • The problem is here:

    if eof then return ()
        else
            linia <- hGetLine wejscie
    

    Indeed, the problem is not whitespace--your issue is that you seem to expect a do block to extend inside subexpressions, which is not the case. The else clause needs to define its own do block:

    if eof then return ()
        else do
            linia <- hGetLine wejscie
    

    There's another error after that, however:

    if (mod licznik liczba) == 0 then
        hPutStrLn wyjscie linia
    przepiszConHelper wejscie wyjscie liczba (licznik + 1)
    

    You're missing an else clause for this if. if is always an expression in Haskell, so it must always evaluate to something. If you want to express "do this IO action if the condition is true, otherwise do nothing" you can use return ():

    if (mod licznik liczba) == 0 
        then hPutStrLn wyjscie linia
        else return ()
    

    The standard library even includes the function when to express this idea:

    when (mod licznik liczba == 0) $ hPutStrLn wyjscie linia
    

    If you wanted, you could rewrite the outer if expression the same way, and get something like this:

    przepiszConHelper :: Handle -> Handle -> Integer -> Integer -> IO ()
    przepiszConHelper wejscie wyjscie liczba licznik = do
        eof <- hIsEOF wejscie
        when (not eof) $ do 
            linia <- hGetLine wejscie
            when (mod licznik liczba == 0) $ hPutStrLn wyjscie linia
            przepiszConHelper wejscie wyjscie liczba (licznik + 1)