On Fri, Oct 15, 2010 at 9:35 AM, <o...@okmij.org> wrote: > > Michael Snoyman wrote: >> I have a recommendation of how to fix this: the MonadCatchIO typeclass >> should be extended to include finally, onException and everything >> else. We can provide default definitions which will work for most >> monads, and short-circuiting monads like ErrorT (and I imagine ContT >> as well) will need to override them. > > It seems that `finally' can be fixed without all these proposed > additions. The method catch is the only necessary method of the > class. > > The subject of catching errors in non-IO monads has a long history, > some of which is documented at > > http://okmij.org/ftp/Haskell/index.html#catch-MonadIO > > The page points out to an old CaughtMonadIO file. I have just updated > it for new Exceptions, and added the final test: > >> test3c go = runErrorT $ go `gfinally` (liftIO $ putStrLn "sequel called") > >> test31 = test3c (return "return" :: ErrorT String IO String) > > *CaughtMonadIO> test31 > sequel called > Right "return" > >> test32 = test3c (error "error" :: ErrorT String IO String) > > *CaughtMonadIO> test32 > sequel called > *** Exception: error > >> test33 = test3c (throwError "throwError" :: ErrorT String IO String) > > *CaughtMonadIO> test33 > sequel called > *** Exception: ErrorException "\"throwError\"" > > As we can see, sequel is always called. Here is the updated file: > > http://okmij.org/ftp/Haskell/CaughtMonadIO.lhs > > Incidentally, one should be very careful of using `finally' with the > continuation monad. The Cont monad lets us ``enter the room once, and > exit many times''. So, finally may be called more than once. We need > the ugly dynamic-wind -- or try to use less powerful monads if they > suffice. >
Perhaps I'm misunderstanding your code, but it seems like it's not really respecting the ErrorT monad at all. Instead, it's converting the error type to a runtime exception, which often times is not at all what we want. A pertinent example: in Yesod, I use a modified ErrorT to allow short-circuiting handler functions to perform special responses such as redirects. Using your code, I believe that such code on my part would result in a 500 server error every time, quite the opposite of what I wanted. I would prefer if the test read as: > test33 = fmap (== Left "throwError") $ test3c (throwError "throwError" :: > ErrorT String IO String) Which never in fact returns True. Or, more to the point, the test is never even called, since the runtime exception prevents it. Michael _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe