On 4 May 2010 07:14, Andreas Raab andreas.raab@gmx.de wrote:
Hi Igor -
I don't think there's a general approach for the problem. After quickly checking our internal uses of callbacks I found that usage basically looks like this:
runCallbackProcess "Run the callback process" [true] whileTrue:[ CallbackSemaphore wait. [self handleCallback] ensure:[self callbackReturn]. ].
handleCallback "Handle a callback" | nArgs rcvr selector args result | nArgs := self callbackGetArgCount. rcvr := self callbackGetArg: 1. selector := self callbackGetArg: 2. args := (3 to: nArgs) collect:[:i| self callbackGetArg: i]. result := [rcvr callback: selector asSymbol args: args] on: Error do:[:ex| ex return: nil. ]. self callbackResult: result.
(this is slightly simplified from original code) The point there is that if there's an error during the callback we simply return some predefined value (nil in the above) and assume that the caller can deal with that default return value.
The alternative (that I had considered but dropped as adding too much complexity for to little value) was having an explicit error indication along the lines of:
result := [rcvr callback: selector asSymbol args: args] on: Error do:[:ex| "Signal underlying code that we failed" self callbackError: ex description. ex return: nil. "still sets a default return value" ]. self callbackResult: result.
But this then assumes that the callback machinery itself has some notion of failure which is generally not the case.
The interesting point here is that since callbacks *can* fail there must be some way by which one can indicate to the caller that a failure has occurred. However, that is exactly why I decided against having the callbackError: call - when you handle the callbacks you absolutely need to wrap them properly in an error handler and do whatever is appropriate to return from the callback in the case of an error. And that, of course, is specific to the callback in question and cannot be implemented by the callback machinery in general.
And if your callback has no way to indicate failure, then you've got two choices: 1) Return some madeup value or 2) Fail and die. There are really no other options.
So the short answer to your question is that to implement a callback properly you *must* specify how to respond in the case of an error. It's not optional and in fact, you might make that part of the interface of a callback, i.e., *always* require an error handler for the client to indicate how to respond to an error. This could be as simple as something like:
Callback action:[self doSomething] ifError:[0]. "returns zero on errors"
or it could be more complex, but you might consider making this explicitly part of the callback interface.
The other issue in this regard is how to debug failures. We punt on this. We print a callstack to indicate error and then we return the default value. So there's no other information than the callstack. You probably want to look at Newspeak to find out how to debug callbacks; there must be a few 'tricks' for how to ensure proper debug and return semantics. We don't have any such uses in Teleplace at this point.
Thanks for detailed explanation. Indeed, there's not much what can be done facing the requirement, that be it error or not, you are still have to return from callback. Since i will demand from callback users to have an unique class for each callback type anyways, then you can override #defaultReturnValue in a subclass and supply value, which is safe to return. And for extreme cases, when you want to be 100% sure, you can always implement callback natively and don't even bother entering an interpreter loop and passing its arguments to a language side. But this is a simple case, which i already having, so its not so interesting ;)
Cheers, - Andreas
On 5/3/2010 4:31 PM, Igor Stasenko wrote: