Handling errors in Haskell
From HaskellWiki
These are the four types of error handling that are standard and widely used in the Haskell world, as of 2014.
There exist some other libraries likeContents |
1 Exception
An unexpected code path, one that rarely but can happen and can be handled if needs be. Typically caused by IO going wrong in some way, like the machine running out of swap and your program terminating, a file not existing, etc. The most basic functions are:
- throw :: Exception e => e -> a
- try :: Exception e => IO a -> IO (Either e a)
from Control.Exception.
Say you were writing a library to do things on reddit, you would define an exception type in your API:
data RedditException = Couldn'tUpvote | CommentFailed | LoginFailed !Text | ConnectFailure !HttpError deriving (Show,Typeable) instance Exception RedditException login :: Details -> IO () login details = do code <- tryLogin details case code of (200,val) -> setLoginContext val (_,err) -> throw (LoginFailed err)
See Control.Exception for more detail and related functions.
2 Error (pure code)
Some pure functions contain calls to error, causing the evaluation to stop and crash:
head :: [a] -> a head (x:_) = x head [] = error "empty list"
case listToMaybe ages of Nothing -> defaultAge Just first -> first
3 Error using the Either type
An expected return value:data ParseError = ParseError !Pos !Text
So this type describes exactly what is going on:
runParser :: Parser a -> Text -> Either ParseError a
main = do line <- getLine case runParser emailParser line of Right (user,domain) -> print ("The email is OK.",user,domain) Left (pos,err) -> putStrLn ("Parse error on " <> pos <> ": " <> err)
Or depending on the code one might opt instead to use a deconstructing function:
main = do line <- getLine either (putStrLn . ("Parse error: " <>) . show) (print . ("The email is OK.",)) (runParser emailParser line)
4 No value using the Maybe type
There is simply no value there. This isn't a problem in the system. It means you don't care why there isn't a value, or you already know.Typical example:
lookup :: Eq a => a -> [(a,b)] -> Maybe b
So one might pattern match on this:
case lookup name person of Nothing -> "no name specified" Just name -> "Name: " <> name
Or use a deconstructing function:
maybe "no name specified" ("Name: " <>) (lookup name person)
lookup "height" profile >>= parseInt >>= flip lookup recommendedSizes