如何使用IO句柄捕获

时间:2015-10-02 09:23:18

标签: haskell

我试图在Leksah的Haskell中构建一个小程序,将.lhs文件转换为.hs文件。但我无法在没有错误的情况下进行构建。任何帮助,将不胜感激。我是Haskell的新手,请原谅我,如果答案很明显,虽然我无法看到它。这是代码和错误:

-- 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

错误是:

 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

2 个答案:

答案 0 :(得分:2)

签名,例如

openGivenFile :: String -> IOMode -> IO Handle

声明openGivenFile返回Handle,除非它抛出异常。

如果您决定在其中捕捉IOError,那本身就没问题,但是你需要某种方法在每种情况下提出Handle - 这是不可能的。

所以,要么你需要让异常流出,要么你需要更改签名,这样你就不再承诺Handle而是更弱的东西了。 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)

经过一些清理后:

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

即使这样也可行:

openGivenFile :: String -> IOMode -> IO (Either IOError Handle)
openGivenFile name mode =
   (Right <$> openFile name mode) `catch` (return . Left)

但是被捕获的异常现在有点隐藏在整个函数的类型签名中。由于这是一个单行,它应该足够清楚,但我更喜欢以前的替代方案,因为它记录了更好地捕捉IOError的意图。

答案 1 :(得分:0)

我让程序运转起来。谢谢志。我把答案标记为答案。这是最终的代码:

编辑:修改了文件结束检查的代码,以便在正常运行时不会出现异常。

-- 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
import Control.Exception (catch)
import System.Environment   -- to allow getArgs

-- Opens a file, given name and mode
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)

main = do args <- getArgs
          (Right fromHandle) <- openGivenFile (args !! 0 ++ ".lhs") ReadMode
          (Right toHandle) <- openGivenFile (args !! 0 ++ ".hs") WriteMode
        --  (Right fromHandle) <- openGivenFile (head args) ReadMode
        --  (Right toHandle) <- openGivenFile (head args) WriteMode
          convertFile fromHandle toHandle
          hClose fromHandle
          hClose toHandle

-- Converts all the lines in a file
convertFile :: Handle -> Handle -> IO ()
convertFile fromHandle toHandle
  = catch ( do ineof <- hIsEOF fromHandle
               if ineof
                  then return ()
                  else 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