While trying to write a program in Haskell, I suddenly realized that I apparently don't understand how error throwing/catching excetions works. While my actual case is significantly more complicated, I've come up with a seemingly minimal example displaying what I don't understand:
import Control.Exception
import Control.Monad
import Data.Typeable
data IsFalse = IsFalse
deriving (Show, Typeable)
instance Exception IsFalse
isTrue :: Bool -> Bool
isTrue b = if b then b else throw IsFalse
catchesFalse :: Bool -> IO ()
catchesFalse = try . return . isTrue >=> either (\e -> fail $ displayException (e :: IsFalse)) (const $ putStrLn "uh-oh")
main :: IO ()
main = catchesFalse False
When running with runhaskell, I would expect the above code to fail and print IsFalse. However, it instead prints uh-oh. On the other hand, if I replace the definition of catchesFalse by
catchesFalse = try . return . isTrue >=> either (\e -> fail $ displayException (e :: IsFalse)) print
then the exception is caught, just as I would expect.
I'm hoping that someone can point me to any resources that could help me understand the discrepency between these two functions. My best guess is that there's something going on with lazy evaluation, but I'm not sure.
If this is indeed the case, what's the best method to force Haskell to evaluate an expression to the point where it could catch an exception? Forgive me, I understand that this particular question likely has many answers depending on what I actually care to evaluate (which, in my actual case, isn't anywhere near as simple as Bool).