3.1 KiB
3.1 KiB
title |
---|
Exceptions, transformers, primitive |
Exceptions, transformers, primitive
Error handling
Let's discuss the different ways you can handle errors in general
Explicit return checking
Examples: C, Go
if err != nil { return err }
Explicit, obvious, tedious, easy to forget
Explicit with sum types
Examples: none?
eres1 <- doSomething
case eres1 of
Left e -> handleError1 e
Right x -> do
eres2 <- doSomething2 x
...
- Forced to consider
Left
case... - Unless you don't care about the result (
putStrLn
)
Sum types with language help
Examples: Rust
let x = doSomething()?;
let y = doSomething2(x);
- Bonus: compiler warning/error on ignored
Result
- Less tedious, difficult to get wrong
- Have to wrangle different exception types
Monad transformer
Examples: Haskell, others?
foo :: ExceptT MyExceptionType IO Y
foo = do
x <- doSomething
y <- doSomething2 x
return y
- Explicit in the exception type
- ... assuming no exceptions in
IO
itself - Still need to wrangle different exception types
Unchecked runtime exceptions
Examples: almost everyone
foo :: IO Y
foo = do
x <- doSomething
y <- doSomething2 x
return y
- No idea what can go wrong
- No tedium
- Much faster than transformers
- In Haskell, this means
SomeException
is thrown fromIO
, always
Reality in Haskell
IO
does have unchecked exceptionsExceptT
overIO
hasSomeException
- My argument:
ExceptT
overIO
, in practice, is a bad idea - Question: if we designed Haskell from the ground up today, would we still include unchecked exceptions?
Nice things in Haskell
This code is nice
timeout tenSeconds someHTTPRequest
So is this
race fileWatcher userCode
How do we get this?
Async exceptions
- Send exceptions to a thread from somewhere else
- No way to control what type of exception may be received
- Therefore: any
IO
action can receive an exception of any type - Necessitates an unchecked exception world
- Once you have that, you have to assume
IO
can fail with anything at any time- Caveat: you can
mask
temporarily for resource cleanup purposes
- Caveat: you can
Dealing with async exceptions
https://haskell-lang.org/library/safe-exceptions
Same approach used by the new unliftio package, docs still in transition
What about transformers?
- Already know
ExceptT
can address async exceptions - Can we deal with synchronous exceptions in transformer code?
- https://www.snoyman.com/reveal/monad-transformer-state
- https://www.yesodweb.com/blog/2014/05/exceptions-cont-monads
Exception handling best practices
The RIO monad
https://www.fpcomplete.com/blog/2017/07/the-rio-monad
FIXME: More up-to-date explanation
Still have time for more?
Let's play with some really fun stuff