I'm trying to build a little program in Haskell in Leksah that will convert an .lhs file to an .hs file. But I can't get it to build without errors. Any help would be appreciated. I'm new to Haskell so forgive me if the answer is obvious, though I can't see it. Here is the code and error:
-- Converts .lhs (literary Haskell files) to .hs (plain Haskell files)
-- Keeps only the statements which are normally compiled, plus blank lines.
-- To use:
-- ghc --make lhs2hs.hs
-- to get an executable file lhs2hs.
-- Then
-- lhs2hs filename
-- will open filename.lhs and save the converted file in filename.hs
-- by Scot Drysdale on 7/28/07, based on SOE program on p. 241
module Main where
import System.IO
-- import System.IO.Error (catchIOError)
import Control.Exception (catch)
import System.Environment -- to allow getArgs
-- Opens a file, given name and mode
openGivenFile :: String -> IOMode -> IO Handle
openGivenFile name mode
= catch (do handle <- openFile name mode
return handle)
handler
where
handler :: IOError -> IO Handle
-- Next line does not match IO Handle
handler ex = putStrLn $ "Caught exception: " ++ show ex
-- (\e -> error ("Cannot open " ++ name))
main = do args <- getArgs
fromHandle <- openGivenFile (args !! 0 ++ ".lhs") ReadMode
toHandle <- openGivenFile (args !! 0 ++ ".hs") WriteMode
convertFile fromHandle toHandle
hClose fromHandle
hClose toHandle
-- Converts all the lines in a file
convertFile :: Handle -> Handle -> IO ()
convertFile fromHandle toHandle
= catch (do line <- hGetLine fromHandle
case line of
('>' : ' ' : rest) -> hPutStrLn toHandle rest
('>' : rest) -> hPutStrLn toHandle rest
('\n' : rest) -> hPutStrLn toHandle line
('\r' : rest) -> hPutStrLn toHandle line
_ -> return ()
convertFile fromHandle toHandle)
handler
where
handler :: IOError -> IO ()
handler ex = putStrLn $ "Caught exception: " ++ show ex
Error is :
src\Main.hs:27:28-69:
Couldn't match type `()' with `Handle'
Expected type: IO Handle
Actual type: IO ()
In the expression: putStrLn $ "Caught exception: " ++ show ex
In an equation for `handler':
handler ex = putStrLn $ "Caught exception: " ++ show ex
In an equation for `openGivenFile':
openGivenFile name mode
= catch
(do { handle <- openFile name mode;
return handle })
handler
where
handler :: IOError -> IO Handle
handler ex = putStrLn $ "Caught exception: " ++ show ex
A signature such as
openGivenFile :: String -> IOMode -> IO Handle
states that openGivenFile returns an Handle, unless it throws an exception.
If you decide to catch IOErrors inside that, that's fine by itself, but you need some way to come up with an Handle in every case -- which is impossible.
So, either you need to let the exception flow out if it, or you need to change the signature so that you no longer promise an Handle but something weaker. E.g.
openGivenFile :: String -> IOMode -> IO (Either IOError Handle)
openGivenFile name mode
= catch (do handle <- openFile name mode
return (Right handle))
handler
where
handler :: IOError -> IO (Either IOError Handle)
handler ex = do
-- since we are returning the error,
-- printing it may be a bad design now
putStrLn $ "Caught exception: " ++ show ex
return (Left ex)
After some cleanup:
openGivenFile :: String -> IOMode -> IO (Either IOError Handle)
openGivenFile name mode = (Right <$> openFile name mode) `catch` handler
where handler :: IOError -> IO (Either IOError Handle)
handler = return . Left
Even this would work:
openGivenFile :: String -> IOMode -> IO (Either IOError Handle)
openGivenFile name mode =
(Right <$> openFile name mode) `catch` (return . Left)
but the exception being catched is a now a bit hidden inside the type signature of the whole function. Since this is an one-liner, it should be clear enough, but I'd prefer the previous alternative in general since it document the intent of catching IOError better.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With