Skip to content

Instantly share code, notes, and snippets.

@evanrelf
Last active October 14, 2020 23:55
Show Gist options
  • Save evanrelf/d9ed5de06ea91766a73c8084ada0d0b9 to your computer and use it in GitHub Desktop.
Save evanrelf/d9ed5de06ea91766a73c8084ada0d0b9 to your computer and use it in GitHub Desktop.
-- Error message with scoping annotation
newtype Error (s :: k) = Error Text
deriving newtype IsString
-- Explicitly opt-in to bubbling up unhandled errors
rethrowError
:: Functor f
=> ExceptT (Error s1) f a
-> ExceptT (Error s2) f a
rethrowError = Except.withExceptT coerce
-- Handle errors (use instead of `catchError` from `MonadError`)
handleError
:: Functor f
=> ExceptT (Error s1) f a
-> (Error s -> ExceptT (Error s) f a)
-> ExceptT (Error s2) f a
handleError = handleError . rethrowError
-- Example function which may throw
divide :: Monad m => Int -> Int -> ExceptT (Error "divide") m Int
divide x y = do
when (y == 0) do
Except.throwError "Cannot divide by zero"
pure (x `div` y)
-- Example of explicitly opting-in to exception bubbling
-- (would not compile without wrapping in `rethrowError`)
showMyNumber :: Monad m => ExceptT (Error "showMyNumber") m String
showMyNumber = do
number <- rethrowError (divide 42 0)
pure (show number)
-- Example of being forced to handle errors if you opt not to bubble them up
-- (would not compile without `handleError`)
topLevel :: ExceptT (Error "topLevel") IO ()
topLevel = do
string <- showMyNumber `handleError` \_ -> pure "handled failure"
putStrLn string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment