On Tue, Oct 19, 2010 at 3:34 AM, Sterling Clover <s.clo...@gmail.com> wrote: > On Mon, Oct 18, 2010 at 8:50 AM, Michael Snoyman <mich...@snoyman.com> wrote: > >> The point here is that we want to treat these things very differently. >> Just take a look at the runHandler code in the Yesod.Handler module: >> runtime exceptions need to be wrapped up in an internal server error, >> whereas "Left" values from the ErrorT get "case"d on. I understand >> that all of this could be done with extensible exceptions, but that's >> really just taking something which is currently explicit and >> type-checkable and making it implicit. It really does seem to me to be >> a typical dynamic-versus-static issue. > > I fail to see how this is a dynamic-static issue. You're already > forced to catch exceptions and wrap them in a MLeft, and then later > you force the MLeft and MRight values in to a uniform representation > which you then case on. Catching a special exception type for exit and > forcing it into the same ultimate union representation doesn't seem > conceptually any more difficult, and in fact removes the need to > reason about two types of exceptions throughout the rest of the code > base. > > I've got a large project that is based on a transformer stack over IO, > and one of my ongoing regrets has been that I went with Either as well > as extensible exceptions -- there's never been a genuine payoff, and > there have been more than a few headaches.
Yesod has used this approach for a long while now, and the only issue has been that buggy "finally" function in MonadCatchIO. I fail to see what's so complicated here, to be honest. If I were to switch, I'd then need to: * Create an Exception instance for HCContent, a datatype which is most definitely *not* an exception. * Start using Typeable to parse out the HCContent stuff from all other exception types. I don't see it shortening the code at all, and it's conceptually wrong: redirection is not an exception. Oh, and I just remembered the reason why it absolutely *can't* work: consider this real life code from haskellers.com: Just uid -> debugRunDB $ do u <- get404 uid mun <- getBy $ UniqueUsernameUser uid case mun of Nothing -> return (uid, u) Just (_, Username _ un) -> lift $ redirect RedirectPermanent $ UserR un This whole block runs as a database transaction (initiated by debugRunDB), and checks to see if a user has a username. If so, the browser is redirected to a URL with the username instead of the user ID. If I used extensible exceptions instead of an Either monad, then that last line would throw an exception, which would cause the database transaction to rollback instead of commit, which is decidedly *not* what we want here[1]. This stresses my point of using the right tool for the right job: you can often be surprised by the strange interactions that will occur when you use something in a way it wasn't intended. Michael [1] True, this database action doesn't actually make any changes, but it's very easy to imagine a case where it would, eg logging. _______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe