Почему `first` из Data.Bifunctor не преобразует это значение

В следующих:

import Data.Bifunctor
import qualified Data.ByteString.Lazy.UTF8 as BLU

safeReadFile :: FilePath -> ExceptT Text IO Text
safeReadFile p = (lift $ doesFileExist p) >>= bool (throwError "File does not exist") (lift $ pack <$> readFile p)

safeDecodeJSONFile :: FromJSON a => Text -> FilePath -> ExceptT Text IO a
safeDecodeJSONFile t f = do
  contents <- safeReadFile f
  tryRight $ first (\x -> pack (x ++ (unpack t))) (eitherDecode (BLU.fromString (unpack contents)))

Когда я бегу runExceptT $ safeDecodeJSONFile "something" "nonExistantFile.json" Я ожидаю получить Left "Does not exist something" но вместо этого я просто получаю Left "Does not exist" - Я знаю, что функция, которую я передаю first выполняется, так как без pack GHC жалуется, что тип (eitherDecode (BLU.fromString (unpack contents))) является ExceptT String IO a вместо ExceptT Text IO a - так почему не конкатенация от ++ тоже бывает?

1 ответ

Решение

Вы написали

safeDecodeJSONFile t f = do
  contents <- safeReadFile f
  tryRight $ ...

Monad экземпляр для ExceptT сдается, как только он ударяет Leftвозвращая именно это. Так что tryRight ... никогда не бывает Вы должны справиться с Left случай явно, возможно, с использованием catchError,

Пока мы на этом, все еще есть проблема. Ты пишешь

safeReadFile :: FilePath -> ExceptT Text IO Text
safeReadFile p = (lift $ doesFileExist p) >>= bool (throwError "File does not exist") (lift $ pack <$> readFile p)

К сожалению, это не надежно. Во-первых, файл не существует, это только одна из причин, по которой он может потерпеть неудачу - могут быть ошибки разрешения, проблемы с сетью для сетевых файловых систем, ошибки устройства, если файл не является обычным файлом, и т. Д. Во-вторых, кто-то другой может удалить файл между временем проверки его существования и временем, когда вы пытаетесь его прочитать. Обычный совет при попытке разобраться с файлами - не проверять сначала. Просто прочитайте файл и перехватите все исключения, используя catch или подобное в Control.Exception или обертки вокруг них

Другие вопросы по тегам