I am writing an application in Haskell and would like to show a meaningful error message to the user if readFile or writeFile fails. I now caught IOError using Control.Exception.tryJust and converted them to human readable text.
However, it is difficult for me to understand what errors I should catch and how to extract information from them. For example, if "/ bin" is a directory and "/ bin / ls" is a file, readFile "/bin" and readFile "/bin/ls/asdf" both give an "inappropriate type", but (in my opinion) they are different errors. In the case of the first, I was able to recover by processing each file in the directory, while the second is more like an error such as "does not exist".
In relation to the previous example, there seems to be no portable way to catch errors of the βwrong typeβ. Looking at GHC.IO.Exception , InappropriateType marked with GHC-only, so I can't just map the pattern by ioeGetErrorType . I could match the match on ioeGetErrorString , but I'm not sure if these strings are always the same for different platforms, compilers, locales, etc.
In conclusion, my questions are:
- What exceptions should be used for
readFile / writeFile ? - Once I have an exception, how should I extract information from it?
- Is there a way to throw GHC exceptions like
InappropriateType ?
Update:
Based on @ErikR's answer, I am looking at the GHC.IO.Exception.IOException fields with the following Haskell program:
import Control.Exception (try) import GHC.IO.Exception (IOException(..)) import qualified Data.ByteString as B main :: IO () main = do try (readFile "/nonexistent") >>= printException try (writeFile "/dev/full" " ") >>= printException try (readFile "/root") >>= printException try (readFile "/bin") >>= printException try (writeFile "/bin" "") >>= printException try (readFile "/bin/ls/asdf") >>= printException try (writeFile "/bin/ls/asdf" "") >>= printException try (B.readFile "/dev/null") >>= printException -- I have /media/backups mounted as read-only. Substitute your own read-only -- filesystem for this one try (writeFile "/media/backups/asdf" "") >>= printException printException :: Either IOError a -> IO () printException (Right _) = putStrLn "No exception caught" printException (Left e) = putStrLn $ concat [ "ioe_filename = " , show $ ioe_filename e , ", ioe_description = " , show $ ioe_description e , ", ioe_errno = " , show $ ioe_errno e ]
Output to Debian Sid GNU / Linux with GHC 7.10.3:
ioe_filename = Just "/nonexistent", ioe_description = "No such file or directory", ioe_errno = Just 2 ioe_filename = Just "/dev/full", ioe_description = "No space left on device", ioe_errno = Just 28 ioe_filename = Just "/root", ioe_description = "Permission denied", ioe_errno = Just 13 ioe_filename = Just "/bin", ioe_description = "is a directory", ioe_errno = Nothing ioe_filename = Just "/bin", ioe_description = "Is a directory", ioe_errno = Just 21 ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 ioe_filename = Just "/bin/ls/asdf", ioe_description = "Not a directory", ioe_errno = Just 20 ioe_filename = Just "/dev/null", ioe_description = "not a regular file", ioe_errno = Nothing ioe_filename = Just "/media/backups/asdf", ioe_description = "Read-only file system", ioe_errno = Just 30