gfoust
gfoust

Reputation: 15

Failing to catch exception thrown by "pure" function

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

Answers (1)

chepner
chepner

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

Related Questions