Reputation: 15
I'm trying to catch a custom exception thrown by "pure" code. I don't understand the behavior of my program.
I'm using GHC 8.6.3. Here's my code:
import Control.Exception
newtype Problem = Problem String deriving Show
instance Exception Problem
foo :: Int -> Int
foo n = n + (throw $ Problem "Whoops")
baz :: IO Int
baz =
return (foo 1)
`catch` \(Problem _) -> return 100
main = do
n <- baz
print n
`catch` \(Problem msg) -> putStrLn msg
I expect the exception to be caught by the first handler, and for the program to print "100". Instead it gets caught by the second handler and prints "Whoops".
Why is the exception being caught in main
and not in baz
? How can I catch the exception in baz
?
Upvotes: 0
Views: 90
Reputation: 531948
Due to laziness, foo 1
doesn't get executed until you actually attempt to print the value of n
. Essentially, n
is bound to the unevaluated thunk foo 1
, not the result of foo 1
.
A rather clumsy way to force baz
to handle the exception is to use seq
; there is almost certainly a more elegant solution.
baz = let result = foo 1
in seq result (return result) `catch` \(Problem msg) -> return 100
And, thanks to @Alec, that more elegant solution is to simply replace return
with Control.Exception.evaluate
in your original function.
baz :: IO Int
baz =
evaluate (foo 1)
`catch` \(Problem _) -> return 100
Upvotes: 2