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?
-- PeterSeibel
Modern Smalltalks have full exception handling. It varies by dialect, but roughly: Smalltalk has a method used thusly:
self 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.
Bar>>method: aFoo instanceVar := [^false] aFoo danceWith: self Bar>>doSeeDo ^instanceVar value Foo>>danceWith: aBar aBar doSeeDo ^self hustleSo 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 "[]"?
-- BillTrost
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."
VisualWorks has
aBlock valueNowOrOnUnwindDo: finallyBlockwhich 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? and PluggableAdaptor. It is OK to include returns in the first kind of block, but not the second kind.
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 with blocks.
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.
StambaughEngineering? 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? --KielHodges
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 3.0 supports 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 BlockContext 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. For example:
[ ... do something with a file ... ] on:FileNotFoundException? do: [:ex | ... 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. So:
[ ... ] 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.
-- ClausGittinger?