2016-01-08 3 views
1

Я пытаюсь написать простую функцию для безопасного чтения файла (если он существует) и ничего не делать, если файл не существует:Haskell Safe IO

safeRead :: String -> IO() 
safeRead path = readFile path `catch` handleExists 
    where handleExists e 
      | isDoesNotExistError e = return() 
      | otherwise = throwIO e 

Это терпит неудачу во время компиляции с

Couldn't match type ‘[Char]’ with ‘()’ 
Expected type: IO() 
    Actual type: IO String 
In the first argument of ‘catch’, namely ‘readFile path’ 
In the expression: readFile path `catch` handleExists 

Это имеет смысл с :t readFile is readFile :: FilePath -> IO String. например, функция, которая возвращает IO StringIO String не то же самое, как IO())

Изменения подписи String -> IO String

Couldn't match type ‘()’ with ‘[Char]’ 
Expected type: IOError -> IO String 
    Actual type: IOError -> IO() 
In the second argument of ‘catch’, namely ‘handleExists’ 
In the expression: readFile path `catch` handleExists 

Который также имеет смысл, поскольку handleExists имеет тип IO()

Чтобы сохранить весь поиск, улов импортируется с: import Control.Exception подпись улова: catch :: Exception e => IO a -> (e -> IO a) -> IO a

Мой настоящий вопрос: как я могу написать такой безопасный, гибкий код в Haskell? Более конкретно, каково было бы изменение, которое я должен был бы сделать для этой функции, чтобы он обрабатывал как случай успеха, так и случай сбоя?

+0

ваш код скомпилирует для меня создание 'isDoesNotexistError' всегда возвращает true, поэтому проблема может быть в противном случае? – Netwave

+0

yep, только что отмеченный, throwIO возвращает 'IO a', а не' IO() ' – Netwave

+4

, мне кажется, что подпись, которую вы ищете, это' String -> IO (Maybe String) ' – Carsten

ответ

5

Вам нужно выяснить, что вы хотите, чтобы ваша функция действительно выполнялась.

Если он успешно читает файл, вы хотите, чтобы он возвращал содержимое в виде строки.

Если это не удается, что вы на самом деле хотите сделать? Вернуть пустую строку или какой-либо другой резервный контент? Затем вы можете просто изменить return() на return "" в первом случае handleExists.

Но если вы хотите указать ошибку в типе возврата, вам необходимо вернуть другой тип, кроме String. Как сказал Карстен, вы можете вернуть Maybe String и дать Just theString для успеха и Nothing. Или вы можете вернуть Either, если вы хотите получить сообщение об ошибке.

Я чувствую, что для этой конкретной функции Maybe String имеет наибольший смысл, поскольку вы только улавливаете отсутствие файла и реверберуете другие ошибки. Тогда ваш код должен выглядеть следующим образом:

safeRead :: String -> IO (Maybe String) 
safeRead path = (fmap Just $ readFile path) `catch` handleExists 
    where 
    handleExists :: IOException -> IO (Maybe String) 
    handleExists e 
     | isDoesNotExistError e = return Nothing 
     | otherwise = throwIO e 

Здесь мы оборачиваем результат readFile в Just выполнить требование типа, а также в случае возврата ошибки Nothing вместо единицы.

+0

Я не думаю, что это сработает так - IMO вам нужно помочь компилятору, предоставив экземпляр «Exception» следующим образом: 'handleExists :: IOException -> IO (возможно, String) ...' ... yep - Haskell жалуется, когда я пытаюсь ваш;) - я позволил себе изменить свое состояние в рабочем состоянии - просто откат, если вам это не нравится – Carsten

+0

, конечно, вам, вероятно, это не понадобится, если 'isDooesNotExistError' имеет право подпись ... Я был ленив и просто сделал 'isDoesNotExistError = undefined';) – Carsten

+0

@Carsten Я понятия не имею, я его на самом деле не собирал. 'isDoesNotExistError' имеет правильную подпись, поэтому я бы предположил, что она компилируется без подписи, но это тоже не повредит. –