On #ifError: considered harmful was Re: Set>>add:
John Sarkela
sarkela at cox.net
Wed Nov 13 00:56:28 UTC 2002
On Saturday, November 9, 2002, at 05:36 AM, Nevin Pratt wrote:
> The final draft of the ANSI standard (and presumably the actual
> standard) specifies that if the argument to #add: for Set is nil, the
> results are undefined.
>
> VisualWorks for this case just returns nil, and otherwise does nothing.
> Squeak throws an exception.
>
> GLORP appears to depend upon the VisualWorks behavior. Since GLORP
> has been ported to VA and Dolphin, I'm guessing that they also have
> VW's behavior. Thus, Squeak appears to be the odd-man out.
>
> I personally am not a fan of exceptions, and use them very sparingly,
> if at all. And in this case, I personally prefer VW's behavior.
>
> But I was curious what anybody else thought about this difference.
I've been meaning to write about appropriate use of exceptions in
Squeak.
Design issues:
1. Need to conform to documented standards for cross dialect developers.
2. Need to conform to actual implementations because in practice,
theory and practice are entirely different things.
3. Need to conform to current platform behavior so as not to impact
existing platform specific code.
Proposed solution:
1. Create an abstract subclass of Error called CollectionError.
2. Create a concrete subclass of CollectionError called
IllegalCollectionValueError.
3. Define IllegalCollectionValueError>>defaultAction as
defaultAction
"Answer the undefined object since no object was added."
^ nil
4. Redefine Set>>add: as
add: newObject
"Include newObject as one of the receiver's elements, but only if
not already present. Answer newObject."
| index |
newObject ifNil:
[IllegalCollectionValueError
signal: 'Sets cannot meaningfully contain nil as an element'].
index := self findElementOrNil: newObject.
(array at: index) ifNil: [self atNewIndex: index put: newObject].
^ newObject
Discussion:
At the core of this solution is the fact that in the Squeak exception
mechanism design
there are *no* unhandled exceptions. If no one in the call chain has
declared a handler
for a kind of exception, then the exception itself will execute its
default action as the root
exception handler for the anomalous occurrence.
Thus, someone attempts to add nil to a Set. Set signals the
IllegalCollectionValueError.
If someone has set an exception handler for a CollectionError their
handler will intercept
and have the opportunity to respond to the exceptional occurrence. If
no explicit
handler has been set, the default action will execute and a value of
nil will be returned.
quod erat demonstrandum
Issues:
There are issues in the base image that need rectification and are the
motivation for the
subject line of this message. In the Squeak exception mechanism, it is
never appropriate
to send the #ifError: message, nor is it appropriate to ever set a
handler on Exception or
Error unless the handler passes the exception after intercept.
(Note: consider the design intention behind the #ifCurtailed: message.
Almost
certainly this is what users of #ifError: truly intend. But I'll leave
that discussion
for a more complete description of the exception mechanism that I have
yet to write.)
A good concrete example of this occurred at Digitalk where this
mechanism was designed
by the Portland team. When the 32 bit VM was ported to Win32s it was
discovered that
when allocating say 10 file handles, the api would return success when
in fact it had
allocated a number of handles strictly less than those requested, for
example 7 were
actually allocated. When the 8th file handle needed to be used, a
FileError would occur.
Interestingly enough, if one used the Win32s api once again one could
allocate more file
handles, just never as many as requested at any one time. Thus, a known
work around
for that error existed, ask Windows for more handles. We were able to
define the
defaultAction for that class of FileError to recognize the condition,
attempt to allocate
more handles and if successful, resume operation. If anyone in the call
chain had said
["block with implicit file ops"]
ifError: [self inform: 'oh my, things have gone horribly wrong!']
"OR"
["block with implicit file ops"]
on: Error
do: [:ex | self inform: 'oh my, things have gone horribly wrong!']
then the particular FileError in question would be caught, precluding
the default handler
for that particular exception from ever having the opportunity to
perform the known work
around to the particular problem.
A better expression would have been
["block with implicit file ops"]
ifCurtailed:
[self inform:
'oh my, things have gone wrong, and no one could recover!']
Note that the argument to #ifCurtailed: is only evaluated when no one
on the call chain, including
the default action, could handle or recover from the error. The point
of #ifCurtailed: is to allow
the entire call chain plus default action to have a chance at recovery
before unwinding
the call chain and if and only if the call chain is unwound, does the
argument to #ifCurtailed:
actually get evaluated.
:-}> John Sarkela
"I know you've heard it before." Dr. Winston O'Boogie
More information about the Squeak-dev
mailing list
|