I was wondering how exceptions are handled in Smalltalk--I'm mostly a Java programmer these days but I've been reading the Smalltalk-80 book recently and as far as I can tell Smalltalk doesn't provide any support for exceptions other than the error message on Object. But that doesn't give anyone up the stack from where the error occurs a chance to deal with it--it just pops up a window or whatever to tell the user there was an error. Or am I missing something?
Modern Smalltalks have full exception handling. It varies by dialect, but roughly: Smalltalk has a method used thusly:
handle: [ :exception | "... code to deal with exception"]
do: ["code that might raise an exception"]
This method answers the result of the do: block if no exception is raised, the result of the handle: block if one is raised.
When things go wrong, you create an instance of Exception and raise
it. The handler chain is searched and the appropriate handler block triggered. --RonJeffries
Smalltalk-80 has a feature with most of the unpleasant side-effects of exceptions. Blocks (closures) can be created, passed around, stored in variables, etc., and these blocks can contain return "statements". If a block is evaluated that contains a return, the method in which the block appears
returns, which can result in the unwinding of arbitrary amounts of stack. Worse yet, Smalltalk doesn't even have the equivalent of a "finally", as far as I know. For example, "self hustle" never gets executed in the following when Bar>>method: is invoked -- doSeeDo makes method: return immedieately.
instanceVar := [^false]
aFoo danceWith: self
So Ron, since you mostly agree with the thesis of this page, do you also avoid ifAbsent: forms of methods and the like as well? When is it OK to put a "^" inside of ""?
Use of blocks as you describe above is in fact, um, problematical, as you observe. Patient: "Doctor, it hurts when I do this." Doctor: "Don't do that."
aBlock valueNowOrOnUnwindDo: finallyBlock
which ensures that finallyBlock will be executed. There's also "ensure" type things around the various STs.
We use ifAbsent: all the time usually inside an object whose responsibility is to reply with a useful answer. Lots of ifAbsent: spread around the system suggests that whatever object is being sent the at:ifAbsent: isn't helping as much as it should.
If you see an ifAbsent: block with a lot of code in it, it's probably trying to tell you that some object isn't supporting you as well as it ought to. --RonJeffries
It is OK to put a return inside of a block when the block is being
used as a control structure, i.e. when it is only being used as a
method parameter and never stored in an instance variable. Blocks
tend to be used in two ways. One way is as a control structure,
as in whileDo: and ifTrue:ifFalse:. The other way is as a way to
parameterize an object, as in SortedCollection?
It is OK to include returns in the first kind of block, but not the
There are exceptions to every rule, of course. An exception to
this rule is usually tricky code, and you should have a good
justification for it.
In fact, the commercial Smalltalk's all have a way of trapping
returns out of context, though each of them do it differently.
Squeak doesn't have one yet. These methods trap exceptions,
death of the process, and returns. If you write your own exception
system in Smalltalk then you can trap exceptions and the death of the
process pretty easily, but I think you have to extend the VM to
trap returns. On the other hand, the couple of times I have had
problems with this, it was always due to exceptions or death of
the process. Experienced Smalltalk programmers rarely play tricks
We use returns inside blocks, at the beginning of a method, when a method needs to return early, ala GuardClause
, or occasionally when a performance measurement has shown the need for an optimization.
Some of my colleagues might use a return in the middle of a method to skip some latter part of the operation, but I recommend using an iffed-out block for that purpose.
offered a product called "Exceptional" that used blocks this way to layer reasonable exception handling into Digitalk & Enfin Smalltalk. What we liked about it was that it required no VM hacking, it was portable, it was easy to fit into Envy/Developer, and we liked its semantics. We instructed our users to avoid embedded returns, so the effect was to gather all the return hacking into one reasonably controllable mechanism. The hardest part was making the debugger and trap handling work, especially across (smalltalk) Process boundaries. --TomStambaugh
What do exceptions look like in Smalltalk? How do you catch them? Can you
selectively catch particular kinds of exceptions while ignoring others?
Every Smalltalk has its own exception handling mechanism, so it is hard
to answer the question "what do exceptions look like". However, the new
ANSI Standard has introduced a new exception handling mechanism, and
presumably all the venders will convert to use it. VisualWorks
both the new version and the old version. The new version looks like a real
improvement. Here is the new version.
The exception handling mechanism relies on two kinds of objects, Signals and Exceptions. Think of a Signal as the type of the Exception. When you raise a Signal, you create an Exception and then go looking for a handler for that Signal. The exception handler is a block with one argument, and it gets evaluated with the Exception as the argument.
A simple example is
[ x / y ]
on: Number divisionByZeroSignal
do: [ :theException | theException returnWith: 0]
You send the on:do: message to a block with a Signal and an exception
handler as an argument.
Note that the Signal is known by the Number class. It is actually stored
in a class variable. When you want to raise this signal, you just say
Number divisionByZeroSignal raiseSignal.
Exceptions have lots of methods besides returnWith:. You can abort them,
restart them, try another exception handler, and so on. Exceptions can
store parameters and messages, and can be subclassed to add more features.
Five minutes searching in Squeak 2.5 (this is already an older version) turned
up the ifError: method on the Block
Context class, which
catches errors thrown with the error: method. Don't know
how long this has been there, though. Arguably, this is an extremely simple
form of exception handling, but it is in there.
One aspect which has not yet been focused upon is that in modern Smalltalks,
(forget about the block-fiddling in ST-80) exceptions are proceedable.
This means, that the exception handler may decide to repair some malfunction
and continue execution after the point where the original exception was raised.
do something with a file
... fetch the file...
ex proceedWith: someValue
proceedable exceptions are exceptionally useful, when things like notifications
or other user-interaction (asking for a missing parameter) have to be aquired from
a deeply nested framework functionality, and needs to be passed through some layers
which are not aware of such interaction. A good example is a parser, which wants to
send some warning message or ask for a defaultable parameter.
BTW: exceptionHandling is (of course) completely save w.r.t. unwinding, thread termination
or nesting of exception handlers.
Also, exceptions form a hierarchy - by providing a handler for a parent Exception, you also
handle all exceptions below.
[ ... ] on:Error do:[ ... ]
catches any error.
For non-related exceptions, HandlerSets?
can be constructed as in:
[ ... ] on:(ZeroDivide? , FileNotFoundExcpetion? ) do:[ ... ]
this handles those two [ , (comma) is a constructor for a Set-of-Exceptions ]
The handlerblock has (of course) access to all kind of fancy information on where and why
the exception was raised. The ex argument in the above example can be asked for the raising
exception, the receiver, the place where the exception was raised and so on.