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
f
to returnEither String Int
. This is similar to returning(int, error)
in Go, but uses Haskell’sEither
type.For the tea-making example, we define a custom
TeaError
type with two constructors:OutOfTea
andNoPower
. This is similar to using sentinel errors in Go.The
makeTea
function returnsEither TeaError ()
. The()
(unit type) is used when we only care about whether an error occurred, not about returning a value.In the
main
function, we use pattern matching withcase
expressions to handle errors. This is similar to checking fornil
errors in Go.We use
forM_
(a variant offor
that discards its result) to iterate over lists, similar to thefor
loops 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.