Let’s get straight to it. Here’s an example of something that I found somewhat surprising in Haskell. First a bit of setup:
printNum n = putStrLn $ "num: " ++ (show n) handleE e = do putStrLn $ "Caught something: " ++ (show e) return (-1) errorP = fromMaybe (error "Got Nothing")
And here it is, in
> CE.catch (return $ errorP Nothing ) handleE >>= printNum num: *** Exception: Got Nothing
And to show that things are lazy we change
handleE e = do putStrLn $ "Caught something: " ++ (show e) return 
Then we can map
errorP onto a list like this:
> CE.catch (return $ map errorP [Just 17, Just 42, Nothing, Just 666]) handleE [17,42,*** Exception: Got Nothing
In neither cas I saw the behaviour I was expecting. A chat on IRC showed that others see this as natural behaviour, explained by laziness. It wasn’t until a night’s sleep that I realised that there still was something that bothered me about that explanation. Another explanation would be that
catch isn’t special. At first I didn’t realise I expected it to be special; I expected it to somehow wrap the evaluation of its first argument so that no matter when it was evaluated any exception raised would be caught. As far as I can see this would be no small feat if laziness is to be preserved. It would require
catch to be special.
So, what does this mean in practice? Well, here are my thoughts. One needs to think carefully about where using
catch makes sense in a program. It has to be inside
IO, a well-documented fact, but it also has to cover something that isn’t lazy since it then will have no effect at all. My gut feeling is that
catch is useful “in the large”, with that I mean as a sort of catch-all “high up” in the program, e.g. in
main. That means its usefulness in libraries is limited (except for IO heavy libs, like FFI bindings to C), it should also probably not be used like try-catch often is used in Python.
I’ve since taken a look at the
ErrorT monad transformer and it seems it behaves like I expected
catch to. That’s for another post though.