|Version 1 (modified by Austin_Hastings, 3 years ago)|
This page documents my (Austin) response to a request from Tene to complain more about exceptions. This is like a "super TT#" in that maybe a bunch of change orders come out of it. (Maybe 0, too.) So other folks please, please chime in with stuff you know, or opine, about how exceptions ought to work.
The point of exceptions is to enable a whole bunch of 'dynamic' (like scope) flow control.
The PCT stuff uses exceptions for things like return, break, and continue, as well as the conventional "a surprising result has occurred." There is a legitimate need for fast, static handling of different types of control transfers. Typed exceptions definitely are one answer to that need. It is conceivable that more types could be added.
Most programming languages support the idea of exception classes. Further, they support catching exceptions based on a class hierarchy: catch an exception of such-and-such class, or any subclass.
An exception class will likely extend a more basic exception class in the language. It is easily conceivable that exceptions may be implemented as roles, or using multiple inheritance.
The current typed system fails to handle this approach. It is unlikely that a single-type-per-exception approach will ever perform adequately in the presence of multiple inheritance and role mixins.
However, the current system is arguably fast - I have no data for this, but if it isn't fast then it has no redeeming value. A single numeric lookup deserves to be fast.
I think the priority has to be given to making 'simple things simple.' For exceptions, that means performance for the control flow exceptions, and reliability for die.
Performance, in this case, can mean whatever the internals guys want it to mean. But in general it should probably mean low cost and getting rid of nested interpreters.
Implementation of 'unusual exceptions' should be optimized not so much for speed, but for convenience. That is, getting rid of inferior runloops, or whatever other thing raises its head.
- The non-local flow, catchable exceptions, and fatal error mechanisms must be extensible - more different kinds of each may be added by user code.
- Non-local control flow needs to be fast, for things like 'break', 'continue', 'return'
- Handling of 'errors' needs to be reliable - no matter how bad the user code, a 'die' type error has to be delivered.
- Catchable exceptions should be handled well, but may trade performance for implementation facility.
A cloud over much of this is coroutines. Coroutines, or some other form of random flow, have to support separate "stacks" for control transfer. That is, if a lexical block throws a "loop break" control exception, the right outer block has to receive it. I suspect this may have already been thought through - I certainly hope so.
That aside, control flow exceptions seem like they are really a different animal, piggy-backing on the exception mechanism. That's not wrong, per se, but it may be possible for control flow stuff to move to a different subsystem with better performance.
In particular, control flow is generally "symmetric" - that is, you don't generate a "next" without knowing there's a "for" somewhere to catch it. (In fact, I suspect most languages know where the for loop is, to boot. Does a control flow implicitly have a target?) In this regard, control flow is probably more like 'longjmp' than the rest of the exceptions mechanism - you know where you're going, and there's a good protocol defined between sender and receiver.
Presently, the throw internals are oriented towards a "nested call" instead of towards the "return the address of the next opcode". There are probably reasons for this, but are they good enough?
There has been some pressure to "make exception handlers be subs" (TT#1091), and the counter-pressure was the handling of control exceptions. If there is validity to the runloop problems being tied to continuations (vice subs), this would be a good opportunity for a split.