Skip to content

Haskell Error Handling Guide — Either, Maybe, Exceptions, and Safe Patterns

DodaTech Updated 2026-06-28 2 min read

In this tutorial, you will learn about Haskell Error Handling Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Haskell error handling prefers pure types (Maybe for absence, Either for error messages) over exceptions -- with ExceptT for transformer-based error propagation in monadic code, and catch/try/bracket for IO Exception Handling.

Maybe for Optional Values

-- Maybe: Nothing or Just value
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)

-- Using with do notation
compute = do
    a <- safeDiv 10 2    -- Just 5
    b <- safeDiv a 0     -- Nothing (short-circuits)
    return b

Either for Error Messages

-- Either: Left error or Right value
type Error = String

validateAge :: Int -> Either Error Int
validateAge n
    | n < 0     = Left "Age cannot be negative"
    | n > 150   = Left "Age seems too high"
    | otherwise = Right n

-- Chaining errors
process :: String -> Either Error String
process input = do
    age <- validateAge (read input)
    return $ "Valid age: " ++ show age

with ExceptT

import Control.Monad.Except

type App = ExceptT String IO

runApp :: App a -> IO (Either String a)
runApp = runExceptT

app :: App ()
app = do
    liftIO $ putStrLn "Enter age:"
    input <- liftIO getLine
    age   <- case readMaybe input of
        Nothing -> throwError "Invalid number"
        Just n  -> validateAge n
    liftIO $ print age

IO Exceptions

import Control.Exception

-- Catch exceptions
safeReadFile :: FilePath -> IO (Either IOException String)
safeReadFile path = do
    result <- try (readFile path)
    return result

-- Using bracket for resource management
withFileSafe :: FilePath -> (Handle -> IO a) -> IO a
withFileSafe path = bracket (openFile path ReadMode) hClose

-- Custom exceptions
data AppError = NotFound String | PermissionDenied
    deriving (Show)
instance Exception AppError

Common Mistakes

1. Using error/undefined in production

error "message" throws an exception that can't be caught in pure code. Use Either or Maybe instead.

2. Pattern matching on Left

Always handle both Left and Right. Forgetting Left cases causes runtime pattern match failures.

3. Not using bracket

File handles and other resources need bracket to ensure cleanup even on exceptions.

Practice Questions

1. What is the difference between Maybe and Either? Maybe signals absence (Nothing). Either carries an error value (Left) or success (Right).

2. How do you throw an error in ExceptT? throwError "message" sets the Left value and short-circuits the computation.

3. How do you safely open a file? Use bracket or withFile which guarantee the handle is closed even if an exception occurs.

FAQ

{{< faq question="Can I catch exceptions in pure code?" >}} No. Exception handling requires IO. For pure error handling, use Either or Maybe. {{< /faq >}}

{{< faq question="What is the difference between catch and try?" >}} catch runs a handler on exception. try returns Left on exception, Right on success (never throws). {{< /faq >}}

{{< faq question="What does bracket do?" >}} bracket acquire release body -- runs acquire, then body, then release (even if body throws). Like try-with-resources. {{< /faq >}}

What's Next

Now learn about testing in Haskell.

Topic Description Link
Testing Testing with HUnit/QuickCheck {{< ref "19-tests" >}}
Parsing Parser combinators {{< ref "20-parsing" >}}

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro