Most modern Smalltalk80 implementations have more or less converged on a standard exceptions framework as set out in the ANSI spec.
There’s a description here (it’s for Pharo, but is more or less the same as for Squeak, GnuSmalltalk etc)
Self doesn’t have an exceptions framework. There is a generally available ‘error:’ message, whose meaning is globally defined; apart from that there is a preference for passing error handlers to methods which are likely to need them (such as ‘at:IfAbsent:’ or ‘do:IfFail:’).
Is this enough?
An ANSI Smalltalk style framework should be doable. The core mechanism is winding up the stack and searching it for a suitable handler, which shouldn’t require any adjustment of the VM. The question is whether we want to.
The plus side is that ports of open source Smalltalk80 code might be made easier. For example, both SUnit and the new Xtreams stream library have built exception handling into the way they do things.
However, even if we adopt the idea of resumable exceptions, our framework would be likely to be different. The ANSI system is class based and relies on hierarchies of exceptions, where an EndOfStream signal is a subclass of an Exception which is a subclass of Error (or vice versa)
For Pharo this looks like:
Blimey!
This of course leads to the well known code pattern of:
| result | result := [(StandardFileStream oldFileNamed: 'error42.log') contentsOfEntireFile] on: FileDoesNotExistException do: [:ex | ex fileName , ' not available']. Transcript show: result; cr
This feels rather unSelflike (unselfish? unselbstlich?). It relies on the identity of objects to determine what happens to them, and also their class membership – confusing the object’s name (ie the path to the object from the lobby) with the object’s identity and behaviour and using a deep class hierarchy as an adhoc pattern matching mechanism.
Part of what I would like Self to do is to move away from unecessary reliance on the lobby as a guarantor of identity, and adopting a rigid lobby based mechanism for determining exception behaviour wouldn’t help.
If the standard class based ANSI exceptions framework is not suitable for Self, what are the options? Is a stack searching resumable exception framework something which should be aimed for at all? Or should the concept of passing exception handlers to objects be explored further?
This reminded me of a comment on LtU some time back on the different kinds of error handling that languages commonly employ, (which I found trying to find articles on a similar line of inquiry about a PL project I was working on).
http://lambda-the-ultimate.org/node/3896#comment-58374
Two of the categories described cover passing error handlers and exceptions respectively, but what I found interesting is that it also pointed out many of the other kinds of error handling and made the beginnings of a cogent point about why you can’t necessarily implement all kinds of software without some support for at least a few of the categories provided. (Though some could be used to implement others.)
I think that this can explain why many frameworks which could use mostly error handler blocks often use exceptions instead, or in addition, since they’re not always trying to make the errors easy to handle anyway. A lot of the time they’re giving you the option to simply let the program die, which I suppose you could simply use a error dialog for, but many programs that these libraries are used for a probably unmanned web servers and they may use the exceptions as part of the logging of errors… (I’m not personally familiar with the behaviour of #error: in self, but I’m assuming that it’s similar to the Smalltalk error dialogs or #halt?)
I think to summarise what I’m talking about, the question is: what happens if you don’t have any way of knowing how to handle the error (in the immediate sender or implementation), but don’t want to forcibly stop program execution?
Thanks,
Lorenz
Go’s approach is interesting. They appear to have adopted a form of exception handing, but are using social engineering to discourage it (by calling it ‘panic’)
The place in Self where the lack of an exception framework has a big impact is in the Morphic GUI. A system of watch-dog threads is used instead to deal with errors and infinite loops.
It would be interesting to compare the current implementation with an alternative based on exceptions (perhaps that is already the case for Squeak?).
I had a quick look at Squeak’s morphic but could only see some small scale use of exceptions – but in any case, exceptions can catch errors but don’t help with infinite loops so I guess that requirement would remain. It’s a bit like how I see #ensure: – it makes it more likely that the finalisation block will be evaluated but can’t be a total guarantee.
Hey,
On my part I’m not that into exceptions^^.
First it is not elegant to write code that intentionally crashes, because you have to do all sorts of cleanup once you do. When raising an exception the stack has to be rewound and rescued, while an explicit error handler simply requires a conditional and a small call.
Second it leads to really naughty style.
At my CS in Java introduction course it was seriously accepted by the profs to run through an array without guards and then use the ArrayOutOfBounds Exception to indicate we’re at the end %D.
Third it will make you brain and language nuts or slow.
Java is my golden example here ^^, the runtime is uber specialized to make exceptions acceptably performant and the language is uber restrictive when it comes to using them, you have to declare them everywhere.
The Java VM might be the logical son of the Self VM when it comes to runtime optimizations, but in my opinion it diverged in exactly the wrong way. If Java had a slogan it would be “Java, The power of complexity.”… sorry got into rant mode 😀
That said I don’t think that exceptions in our case would feel that different from fail blocks.
I mean what is the huge difference between.
java:
try{
file.open();
}
catch{
print(“halp!”);
}
translated to self:
[file open] onFail:”fileNotFound” do:[“halp!” print]
and
[file openIfFail:[“halp!” print]]
Most other languages have to do this because the don’t have blocks.
Sure the exception will still deliver when there are calls between the exception causing and the exception handling object but the one who called the failing method would know best what to do anyway.
Other objects should not have to do the dirty work for the ones who screwed it up^^. I think it might even become harder to debug because you tend to drag errors through the entire program structure before they might get handled.
But I totally agree with Jecel that Morphic has a serious flaw, as a single bad baseDrawOn: must not bring down an entire world.
Still I have to note here that the debugger is so awesome that I was almost always able to revive my world. The cases where I couldn’t were junked worldMorphs and deleted references to my canvas, whops.
—as a side node here I am currently thinking hard about how to solve this without exceptions^^– –and I might have found a way below :P–
It would be cool if a flaw in a baseDrawOn: would simply result in a black box with a white scull drawn instead of the morph 😀
Maybe we could create safe blocks for which we don’t care what went wrong.
[mah save block] onFail:[mah failsafe block]
As far as I have seen people using Exceptions most of them don’t care what went wrong. They don’t check the type of the exception.
This way we would still provide the normal failsave blocks and still have the possibility to survive in critical parts where the shit hits the fan and we don’t care why (as in baseDrawOn:).
This would also be in harmony with my first point since you could not write code that would intentionally crash and thus eliminate the second and third 😀
Phew that’s my 5 cents. I may be totaly wrong :), since I was draged between “ok lets do this” and “nooo were going to end like java” during the writing of this 😀
I wish you all a good night (3:15 Am here).
Cheers Jan