Towards a better conceptual model of exceptions (Was: Re: The Right Approach to Exceptions)

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Feb 21 11:00:52 PST 2012


On Tue, Feb 21, 2012 at 06:24:01PM +0100, deadalnix wrote:
> Le 21/02/2012 18:10, H. S. Teoh a écrit :
[...]
> >True, and there's nothing to stop you from digging into the details
> >of the raised Condition if you want to. I did consider implementing
> >Conditions as some kind of class hierarchy, so that the generic
> >categories are at the top, underneath Condition, then actual specific
> >Conditions can extend them. If your handler knows of a specific
> >Condition, then you can access any pertinent additional info, and
> >make decisions based on that.
> >
> >But what I wanted to know was, can you still do something meaningful
> >even if you knew nothing beyond the top-level generic categories of
> >Conditions? That way, your handler will still work with new
> >Conditions that you've never seen before.
[...]
> And here come an idea of mine :
> 
> If the Condition has a way to provide the Exception associated. So you
> can get the hierarchy from the Exception, and you don't need to creat
> two hierachy of classes just for the bloat.

You're right, that would be unnecessary duplication, especially since an
unhandled Condition becomes a thrown Exception anyway, and it's a very
bad idea to duplicate the entire Exception hierarchy in Condition.

Only thing is, then the handler will have to downcast the Exception to
get to the useful info. This may lead to messy code. But it's something
we should investigate.


> You'll fond attached some sketching code. What do you think of it ?

Is this the same code you attached in an earlier post? I did look over
it, sorry, didn't have time to respond earlier. I like the idea of
wrapping retryable code in the runTransient template, that way we
minimize the amount of boilerplate needed to actually use this feature.

Oh wait, you've added some new stuff. Let's see...

Hmm, I like the idea of providing default handlers for some
commonly-encountered situations. Reduces the amount of boilerplate.  And
there's always the option of manually defining a handler if you need to.
+1.


I was considering more last night how to implement this system. I think
I change my mind about having a common Condition base class. The whole
idea is that every major category would be defined by what kinds of
actions are available, so they are quite distinct from each other. I
don't want to reinvent another hierarchy to represent problems; we
already have an Exception hierarchy for that. So the different
Conditions need to be qualitatively different.

To maximize usability and minimize redundancy and bloat, I'm thinking we
should define categories based on what recovery actions are *actually
available*, rather than what actions are *potentially* available. So to
that end, it's not really categorization per se, but more of a way of
describing what recovery strategies are actually available.

In other words, an input error where you can recover by skipping the bad
data is qualitatively different from an input error where skipping
doesn't fix the problem. These two should be treated as distinct
Conditions. Basically, I want to guarantee that for some Condition c,
action A will *always* be available to the handler. Then the handler
won't need excessive checking (if A1 is available but A2 is not, then
use A1; else if A1 is not available but A2 is available, ... etc.). It
can count on all options being actually present.

Back to your code. We can implement this idea by defining a template for
each of the possible conditions. So code in runTransient always raises a
transient condition if it fails, runReroutable always raises a
reroutable condition if it fails (reroutable = failing component X can
be replaced by component Y). I don't know if this is too inflexible, but
trying to mix multiple conditions into a single operation seems to turn
the code into spaghetti:

	int x, y, z;
	retry1:
		doSomething(x, y, z);
	retry2:
		if (problem1)
			raise(condition1);
		else if (problem2)
			raise(condition2);
		...
	handleCondition(condition1):
		if (recoveryAction1) {
			fiddleWith(x);
			goto retry1;
		} else if (recoveryAction2) {
			doSomethingElse(x,y,z);
			goto retry2;
		}
	handleCondition(condition2):
		if (recoveryAction3) {
			fiddleWith(y);
			goto retry1;
		} else if (recoveryAction4) {
			fiddleWith(z);
			doSomethingElse(x,y,z);
			goto retry2;
		}

So I don't think this is the right way to go. Each operation should have
a single condition with a well-defined set of recovery methods, not some
arbitrary combination of multiple conditions.

What do you think?


T

-- 
The day Microsoft makes something that doesn't suck is probably the day they start making vacuum cleaners... -- Slashdotter


More information about the Digitalmars-d mailing list