Another term being coined on the spot; if anyone is aware of a better name for this DesignPattern/LanguageFeature?, please ReFactor this appropriately.
A DeferredExceptionObject is a useful way of doing error-handling, with some of the advantages of ExceptionHandling and some of the advantages of other more lightweight means of handling errors (such as returning a DistinguishedObject? to indicate failure). A DeferredExceptionObject is a DistinguishedObject? with the property that any attempt to do anything with it, other than a few "safe" instructions such as testing for (in)equality, etc., causes a corresponding exception to be thrown (it may well be that the DeferredExceptionObject itself may be thrown). Suppose we have the following bit of pseudo-code, which looks up an object in an AssociativeContainer? by a key k:
v := container.lookup(k);
Suppose, further, that there is no matching key in the container. What should lookup do? A couple of possibilities:
- Throw an exception. Many languages incur a penalty if you do this; plus a "no such key" condition is frequently an expected rather than an unexpected condition when performing a lookup. It would be a royal pain to have to wrap every call to lookup in a try block (or whatever your favorite language provides) just to handle missing keys. The good thing about this is that it makes it harder to ignore the error (you could use an EmptyCatchClause, but that's a no-no in most books).
- Return a tuple of <found,value>. The C++ StandardTemplateLibrary does this for its associative containers. This works, but it's easy to ignore the error; and the "value" field may contain nonsense if the "found" field is false.
- Can you cite an instance in the STL where this is done? I think you may be mis-remembering insert, which returns pair<bool,iterator>. In this case, the bool indicates whether the item already existed; the iterator is always valid. Find operations typically return an iterator, which can be tested against end() to determine if a match was made.
- Return an error code/status; use an "out" parameter to contain the value. Similar to the tuple solution above; makes it harder to use the lookup in an expression.
- Return a DistinguishedObject? - one not in the domain of the container. In some languages, this might be easier said than done.
- Return a NullPointer. Works if null isn't in the domain of the container (but is a legal value to pass in); but in many cases a) null is a valid value for the container to hold, or b) may not be stored in the return type. When null does work, it can resemble the DeferredExceptionObject (as dereferencing it produces a NullPointerException in many languages - figuring out why NullPointerException was raised can be a pain though).
Or, we can use a
DeferredExceptionObject. Within the container implementation; a
DeferredExceptionObject is created (
not raised); it can contain relevant context info - i.e.
if (keyNotFound)
return new DeferredKeyNotFound? (key, this);
else
return value;
The client, depending on whether or not the not-found condition is expected; can do two things:
v := container.lookup (k);
if (v.isException())
{
// handle not found case
} else {
v.frozzle();
}
- If a not-found condition is not expected; just use the result. If not-found occurs, the exception is then raised.
Examples of this in the wild:
- The SignallingNan, defined by the IEEE754 floating point standard, is a DeferredExceptionObject with hardware support on most architectures.
- Likewise with the NullPointer; though null gives you no context whatsoever.
- The NotInitialized? value in JavaScript (did I spell that right), which variables get initialized to if not explicitly initialized.
- DoesNotUnderstand, and other error messages in Smalltalk, have this behavior.
The description of this pattern share large parts with some implementations of NullObject (see NullObjectImplementation). Copied from there:
''The initial motivation for this class was to get rid of these useless NullPointerExceptions, that occurred if somewhere a 'null' was returned, not handled (as it should be) and passed on and on, until sometime later the NPE was thrown and nobody knew why.
I decided to let the Null object (explicitly no singleton) know where and why it was created by storing an exception on its creation. This exception was then used on method calls, that could not produce trivial results (empty collections etc. as above) to throw a NPE, that had as the ultimate cause the stored exception. This way these common and hard to detect error cases were easily locatable.''
The application of this pattern for clean initialization (as listed above under NotInitialized? in JavaScript) is also powerful. Complex systems tend to have complex initialization mechanisms. The startup of a monolithic system as well as that of a loosely coupled container managed system can benefit from this pattern. Especially so if the setup contains (potential) cycles. Problems during initialitation like
- missing dependencies to requrired resources
- long chains (cycles) of dependencies ending in an unitialized component
- useless error messages in case of components missing initialization
can be mediated by using this pattern. The idea is to replace not-yet initialized fields with such a
DeferredExceptionObject that tells what (component, source location) is uninitialized (and maybe why).
Thereby incomplete initialization shows up as a useful message instead of a
NullPointerException or undefined behavior or else.
Since your intended data and the deferred exception type must be of the same type most languages, you're essentially re-inventing Haskell's Maybe type. --SamuelFalvo?
See also SentinelPattern
CategoryException