Errors in Haskell
In Haskell, error handling is typically done using the Either type or custom error types. We’ll demonstrate both approaches in this example.
import Control.Monad (forM_)
import Data.Either (isLeft)
-- By convention, we return Either Error Result,
-- where Error is on the left and Result is on the right.
f :: Int -> Either String Int
f arg
| arg == 42 = Left "can't work with 42"
| otherwise = Right (arg + 3)
-- Custom error type for tea-making errors
data TeaError = OutOfTea | NoPower
deriving (Show, Eq)
-- Sentinel errors
errOutOfTea :: TeaError
errOutOfTea = OutOfTea
errPower :: TeaError
errPower = NoPower
-- Function that may produce a TeaError
makeTea :: Int -> Either TeaError ()
makeTea arg
| arg == 2 = Left errOutOfTea
| arg == 4 = Left errPower
| otherwise = Right ()
main :: IO ()
main = do
-- Testing the f function
forM_ [7, 42] $ \i -> do
case f i of
Left err -> putStrLn $ "f failed: " ++ err
Right result -> putStrLn $ "f worked: " ++ show result
-- Testing the makeTea function
forM_ [0..4] $ \i -> do
case makeTea i of
Left err -> case err of
OutOfTea -> putStrLn "We should buy new tea!"
NoPower -> putStrLn "Now it is dark."
Right _ -> putStrLn "Tea is ready!"In this Haskell version:
We define
fto returnEither String Int. This is similar to returning(int, error)in Go, but uses Haskell’sEithertype.For the tea-making example, we define a custom
TeaErrortype with two constructors:OutOfTeaandNoPower. This is similar to using sentinel errors in Go.The
makeTeafunction returnsEither TeaError (). The()(unit type) is used when we only care about whether an error occurred, not about returning a value.In the
mainfunction, we use pattern matching withcaseexpressions to handle errors. This is similar to checking fornilerrors in Go.We use
forM_(a variant offorthat discards its result) to iterate over lists, similar to theforloops in the Go example.Error checking in Haskell is explicit through the use of
Either, similar to Go’s approach of returning errors as values.
To run this program, save it as Errors.hs and use:
$ ghc Errors.hs
$ ./Errors
f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.This Haskell code demonstrates error handling techniques that are idiomatic to Haskell while maintaining the spirit of explicit error handling from the original example.