2015-10-02 4 views
3

Я пытаюсь создать небольшую программу в Haskell в Лексах, которая преобразует файл .lhs в файл .hs. Но я не могу заставить его строить без ошибок. Любая помощь будет оценена по достоинству. Я новичок в Haskell, поэтому простите меня, если ответ очевиден, хотя я не вижу его. Вот код, и сообщение об ошибке:Как использовать catch с ручкой IO

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

'putStrLn' возвращает' IO() '. Я не знаю, почему вы ожидаете, что это сработает. Если вы ожидаете возможности отказа, возвращайте 'IO (Maybe Handle)' - добавьте 'Just' к основному пути и' return Nothing' вашему обработчику –

+0

Спасибо. Я знал, что это был ИО, но я не знал, как его адресовать. Еще раз спасибо. – te7

ответ

1

Подпись таких как

openGivenFile :: String -> IOMode -> IO Handle 

утверждает, что openGivenFile возвращает Handle, если это не вызывает исключение.

Если вы решили поймать IOError s внутри этого, это прекрасно само по себе, но вам нужно каким-то образом придумать Handle в каждом случае - что невозможно.

Таким образом, вам нужно предоставить исключение, если оно, или вам нужно изменить подпись, чтобы вы больше не обещали Handle, но что-то более слабое. Например.

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.

+0

Я использовал первый пример, который вы дали, и это сработало. Благодарю. Но тогда функция convertFIle не распознала переменные fromHandle и toHandle. Я изменил объявление функции на «convertFile :: (либо IOError Handle) -> (либо IOError Handle) -> IO()», и переменные распознаются, но теперь hGetLine завершился неудачей с новым типом переменной (отличным от ожидаемого типа Handle). Есть идеи? – te7

+0

Если результатом является 'Left ioError', вы должны остановить то, что вы делаете, и обработать его. Не имеет смысла называть 'convertFile', если у вас нет двух фактических« Ручек ». Дело в том, что код выше просто переводит исключения в тип «Либо». Вы можете перенаправить это, тем самым отложив обработку ошибок, сколько хотите. Но в вашей программе будет какой-то момент, когда вы захотите проверить, что находится внутри этих '' '' '' '' '' '' '' 's' s: если это 'Left', остановитесь, если это' Right', возьмите ручку и продолжайте. – chi

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