
Well, since you happened to ask me (although I am as good a bikeshedder
as the next person), I think that you don't need to mention Async
anywhere in the module name, since the module doesn't make any attempt
to differentiate between sync and async exceptions.
And the fact that the module uses the 'async' library is just an
implementation detail.
I'd go with something like Control.Exception.CatchAny.
Roman
* João Cristóvão
Roman,
By mere chance today I was, about the same time you published your library, working on the suggestion made by Michael in the end of his original blog post: splitting the async exceptions part from classy-prelude (he is ok with this).
https://github.com/jcristovao/async-exception
I was not yet sure about the namespace, I had opted for: Control.Concurrent.Async.Exception
But yours makes more sense, Control.Async.Exception
I agree that the two solutions address different problems, and as you say, for controlled situations where performance is critical yours indeed adds less overhead. But for more general solutions, Michael's solution - split from Classy prelude, seems to be the way to go, and thus my 'split' makes sense if you don't need the remaining classy prelude.
As such, I was considering the namespace: Control.Async.Exception.All
To differentiate from yours, signaling that it handles _all_ exceptions. What do you think?
Anyhow, I also think Joachim suggestion (of at least implementing the new exception classes in base-compat) makes sense, so I volunteer to add to the work I already done here: https://github.com/sol/base-compat/pull/2
(If the patch gets accepted, of course).
Cheers, João
2014-02-05 Roman Cheplyaka
: Ok, this clears things up. I misinterpreted your approach thinking that you're also solving the problem of distinguishing async vs sync exceptions, only based on how they were thrown instead of their type.
I now see that it isn't the case -- you're catching *all* exceptions. (And run the timeout handler in a different thread.)
So no wonder that asynchronous-exceptions (whose description says that it lets differentiate between sync and async exceptions, in a certain sense) doesn't help you -- you simply don't want any exceptions at all.
My use case is simpler -- I write testing libraries. If a test throws an exception, we have to decide whether we want to report it as a test's failure or it's a bigger problem and we want to wrap up.
I don't think there's a universally right way to make this decision. It depends on what exceptions exist and what threads they can be thrown to. E.g. if there existed something like UserInterrupt but which could be thrown to any active thread, not only the main thread, then the approach "run in a separate thread and log any exceptions from that thread" simply wouldn't work.
For tasty, based on the async exceptions I'm aware of, I think your approach is overall better. It's almost as simple, doesn't require patching 3rd-party timeout libraries, and catches StackOverflow (which is desirable). So I'll switch to it instead.
For smallcheck, the overhead of forkIO might be significant, because it has to be performed for every single property check, and those can be numerous and very quick. I put together a simple benchmark (http://lpaste.net/99532 if anyone is interested) which shows that overhead can be noticable (16% for async vs 4% for simple catch) but tolerable, and it will be even less for more realistic properties. So I'll probably use the async approach there, too, although I may reconsider that in the future if I ever get to optimizing smallcheck and squeezing out those percents.
As for the package itself, let's see if others will find any good use cases for it. I'll update the docs with some conclusions from this thread.
And thanks for your input.
Roman
* Michael Snoyman
[2014-02-05 18:48:22+0200] I can't think of any situation in which the semantics you're implying make sense. To me, catching synchronous exception is a simple concept: if an exception is generated internally to `userAction`, then it's a synchronous exception. If it was terminated by something external, then it's asynchronous. I'm not sure what you're getting at about my approach requiring knowledge of what's going on deep inside a library.
The real question which is not explained in your package is what use case you're actually trying to address. Here's a prime example I've run into: you're writing a web application which uses a third-party library. If that library throws an exception of any type, you want to catch the exception and display an appropriate error message (or perhaps return some data from another source). However, we still want the web application to respect timeout messages from the server to avoid slowloris attacks. The handler code would look like:
myHandler = do eres <- tryAnyDeep someLibraryFunction case eres of Left e -> tellUser "I'm sorry, there was an issue making the query" Right x -> displayData x
The goal is that, under no circumstances, should someLibraryFunction be able to case the exception to escape tryAnyDeep. This includes rethrowing some async exception that it received from, e.g., a timeout. This would not be honored by trySync.
Michael
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe