OASIS Mailing List ArchivesView the OASIS mailing list archive below
or browse/search using MarkMail.


Help: OASIS Mailing Lists Help | MarkMail Help



   Re: [xml-dev] The subsetting has begun

[ Lists Home | Date Index | Thread Index ]

On Sunday 23 February 2003 17:34, Karl Waclawek wrote:

> > Many languages have just that construct. "break" is a nonlocal exit, just
> > like throw / catch.
> It doesn't unwind the stack.

Depending on the language :-) Most big languages disallow breaks across 
procedure boundaries to avoid the issue, but you can escape out of regions 
that have their own local variables in.

> > Then you have continuation-passing languages where procedure return
> > points are first class objects; you explicitly pass the return address
> > into a procedure, and there's nothing stopping you from passing in
> > several, one for success and one for each different kind of failure, that
> > return to different points in the stack.
> Now that might be a nice feature to have in the popular languages.
> Which language are you talking about?

Hrm... Scheme can do it, that's probably the most accessible example, but 
IIRC ML can. At least, the default book on implementing continuation 
languages is based around ML. "Compiling Continuations", it's called.

> In principle I can agree.
> I usually look at it that way: If an operation throws an exception
> I consider it failed: Examples:
> - When a constructor does it, I assume the object was not created.
>   Making the wrong assumption here will cause a memory leak in languages
>   without garbage collection.
> - When I open a file, and that throws an exception, I assume it is not
> open, so I don't need to close/unlock it.

Some languages have the concept (and I don't mean it's part of the syntax, 
just part of the conventions of how it's used) of each exception having an 
explicit description of what state things will be in afterwards, and what 
restarts are applicable.


Well, in Java, an exception handler is executed in the environment it is 
declared in, rather than where it's thrown. That means that if you rethrow an 
exception from a catch block, the catching of that exception begins in the 
stack frame of the try statement, not of the original throw.

But in some languages (Dylan and Lisp come to mind), the exception handler is 
executed 'within' the throw statement.

In practice, that means you can do tricks like define, in your file-access 
library, an exception called 'FileNotFound' that is thrown by 'File open 
(String filename)', and a series of restarts that are legal within a 
FileNotFound handler:

 1) ReturnThisOpenedFileObject (File file)
 2) TryAgainWithThisFileName (String filename)

The restarts work just like exceptions (common base class of Exception and 
Restart called, say, Signal); the code for open() might look like:

while (true) {
	try {
		File f = _real_open (filename);
		if (f == null && _errno = ERROR_FILENOTFOUND) {
			try {
				throw FileNotFound ();
			catch (ReturnThisOpenedFileObject r) {
				f = r.file;
		return f;
	} catch (TryAgainWithThisFileName r) {
		filename = r.filename;

Of course, you can still do non-local exits from exception handlers by not 
throwing any restarts.

> From this point of view I would assume that an XML document is in error,
> or an external entity could not be resolved, if parsing the document
> throws an exception.

Yep... that's one perfectly valid point of view :-)

But in things like software interfaces, where there is still an element of 
style in design as well as logic, other people may have different styles to 
us and, sadly, we must be prepared for foreign interfaces to come from 
different points of view :-(

> Obviously, it is not always so clear what the definition of a failure is,
> it depends on the kind of operation and possibly the context.
> That is why I tend to not throw exceptions in low level libraries,
> since the context in which the library is used often determines if a result
> of an operation is a failure.

Using an open() call simply to test for file existance is a classic example!

> > Is that under Windows? ISTR that Windows has to handle some part of
> > exception handling itself for some obscure reason and makes it awful
> > slow?
> Java seems to be slow too. I tried the same on Kylix/Linux.
> Was actually 10 times worse. So, on Kylix the ratio is more like 1000
> as was reported for Java (don't know which platform).
> Cannot guess as to why.


> > Exeptions don't *need* to be slow. There are fast implementations.
> I am curious - which ones?

Well, ones that work in continuation languages... continuation systems, by 
not having a stack, eliminate the stack unwinding. Without having the concept 
of a procedure return hardcoded into the language, instead having a more 
generic construct that implements procedures-that-return, exceptions, 
context-switches-for-coroutines and so on, they manage to do all of these 
things with about the same speediness.

The cost is that you get more memory allocation and pretty much need garbage 
collection, so tricks need to be used to make sure that's done efficiently.

There are issues in how you define the semantics of the pattern matching used 
in exception catching, too. A naive implementation of throw would be to walk 
up a linked list that represents the dynamic scope of try blocks we are 
wrapped by, then invoking some complex algorithm to see if the exception is a 
subclass of the things each try wants to catch, which can get time consuming 
in itself. However, you can make more of this static; going to the opposite 
extreme, statically deduce the set of exception classes that are ever thrown, 
then for each thread have an array of linked lists of handlers, one array 
element per exception class. Then a try...catch for exceptions that are 
subclasses of X would, before entering the protected block, push the handler 
onto the linked list for all classes that are subclasses of X. This would 
raise the cost of entering a try...catch block, but it would make handling 
the exceptions pretty quick!

> How fast can they be - if the problem is inherently more complex,
> then implementations tend to be more complex and slower too.

I think the runtime algorithm required to match the thrown exception to the 
correct handler is probably the most inherently complex part. The actual 
control transfer can be done most efficiently with a stored failure 
continuation. Indeed, for a specific case like stopping SAX processing, you 
could just put an 'abort' continuation in thread-local storage before 
starting the SAX parser, and just invoke it when needed in the SAX handler :-)

> Karl

A city is like a large, complex, rabbit
 - ARP


News | XML in Industry | Calendar | XML Registry
Marketplace | Resources | MyXML.org | Sponsors | Privacy Statement

Copyright 2001 XML.org. This site is hosted by OASIS