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

deadalnix deadalnix at gmail.com
Tue Feb 21 11:56:03 PST 2012


Le 21/02/2012 20:00, H. S. Teoh a écrit :
> 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.
>

Yes, I'm aware of the problem and it is real. I have no good solution 
for it now.

>
>> 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.
>

Yes indeed. Exception should provide data about what the problem IS, 
condition on what are option to recover.

> 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.
>

Agreed.

> 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?
>

It is exactly what lead me to create template for such cases. 
Additionnaly, thoses templates should include a way to generate the 
Exception, but I ran into a strangeness of D when exprimenting with and 
did had time to come up with something working for that. The condition 
handling and the actual operation must be separated to avoid code 
duplication and ensure separation of concerns.

The current design allow to create new Conditions, in the lib but also 
in user code.

If we can find a way to handle properly the Exception crazy casting 
problem we have here a very nice way to handle errors.


More information about the Digitalmars-d mailing list