New exception type needed for Java 7?

3 views
Skip to first unread message

Charles Oliver Nutter

unread,
Oct 20, 2007, 12:59:59 AM10/20/07
to jvm-la...@googlegroups.com
With more and more discussion about closures and non-local flow control
and various stack-manipulating events becoming part and parcel on the
JVM, it seems like we need to introduce a new exception type.

There's two reasons for this:

- catch (Throwable t) or catch (Error e) would break current non-local
flow control mechanisms based on exceptions or errors
- some of you whiners don't like us using exceptions or errors for
non-local flow control

So, what about a new type of exception-like feature: Jump

public class Jump {}

public class NonLocalReturn < Jump {
Object returnValue;
}

Jumps would not be catchable as either errors or throwables, though they
would still trigger finally blocks. Because they'd be a new class of
exception-like feature, no existing code would be incorrectly trying to
catch them. And they're explicitly stated to be used for these sorts of
"jump" events.

Thoughts?

- Charlie

Neal Gafter

unread,
Oct 20, 2007, 1:08:26 PM10/20/07
to jvm-la...@googlegroups.com
I like this. The exception tables presumably would be allowed to contain entries for subtypes of Jump.

Frank Wierzbicki

unread,
Oct 20, 2007, 1:12:53 PM10/20/07
to jvm-la...@googlegroups.com
On 10/20/07, Charles Oliver Nutter <charles...@sun.com> wrote:
> So, what about a new type of exception-like feature: Jump
>
> public class Jump {}
>
> public class NonLocalReturn < Jump {
> Object returnValue;
> }
>
> Jumps would not be catchable as either errors or throwables, though they
> would still trigger finally blocks. Because they'd be a new class of
> exception-like feature, no existing code would be incorrectly trying to
> catch them. And they're explicitly stated to be used for these sorts of
> "jump" events.
This sounds like a great idea. It uses existing structures in a new
way that could then be optimized for the needs of non-java languages
at the VM level. I can't see a downside.

-Frank

Patrick Wright

unread,
Oct 20, 2007, 1:32:38 PM10/20/07
to jvm-la...@googlegroups.com
The idea is interesting, but, two points

1) Where does it jump to? The next line after the method invocation?
Or a finally block, if there is on? What if I haven't defined a
finally block in the enclosing (method-calling) scope? Does it bubble
up to some other finally in a broader scope? What if I'm starting with
a script and haven't defined any finally at all? I guess the compiler
could detect that a Jump was being declared (how?) and force you to
include a finally.

2) The word "jump" seems to imply a location (jump-to, jump-back-to)
but no destination, specifically.

I'm probably missing the larger discussion here, though I've been
following some of the talk on closures. Would be interested to hear
more.


Thanks
Patrick

Charles Oliver Nutter

unread,
Oct 20, 2007, 2:07:02 PM10/20/07
to jvm-la...@googlegroups.com
Patrick Wright wrote:
> The idea is interesting, but, two points

Good points...

> 1) Where does it jump to? The next line after the method invocation?
> Or a finally block, if there is on? What if I haven't defined a
> finally block in the enclosing (method-calling) scope? Does it bubble
> up to some other finally in a broader scope? What if I'm starting with
> a script and haven't defined any finally at all? I guess the compiler
> could detect that a Jump was being declared (how?) and force you to
> include a finally.

The typical way a non-local return (for example) is implemented now is
by constructing the closure containing it such that it has a unique ID
to return to. NonLocalReturn would then be constructed with this ID, and
the jump would bubble back out through the stack until someone
interested in NonLocalReturn events caught it and saw it had the
appropriate id, at which point the return value would be unwrapped and
bubbling would stop.

So it is still more of a "throw" or "raise" or "trigger event", but the
intention is that somewhere, someone will be interested in being the
"landing point" for the jump.

Now this is just the brute-force way we've been implementing it. It
could certainly be what the actual JDK7 generates as bytecode, ideally
all behind the scenes when you do a return within a closure. However I'd
wager there are specific optimizations that could be done to simplify
it. For example, exception tables could be generated such that they're
only interested in the jumps generated in that scope, both types and
IDs, allowing other jumps to transparently pass through without the cost
of catching and re-throwing.

Neil Gafter: I recall you advocating non-local returns, but I don't
remember if you also advocate other types of non-local flow control like
break or continue. Do you?

myList.each {(String element) if (element.equals("foo")) break; }

> 2) The word "jump" seems to imply a location (jump-to, jump-back-to)
> but no destination, specifically.

Perhaps it's more like "JumpAndHopeSomeoneWillCatchYouOnTheWayDown". I
think Jump is a bit nicer though.

The accurate name would probably be "BeginStackEscape" or something similar.

- Charlie

Charles Oliver Nutter

unread,
Oct 20, 2007, 2:19:13 PM10/20/07
to jvm-la...@googlegroups.com
Neal Gafter wrote:
> I like this. The exception tables presumably would be allowed to contain
> entries for subtypes of Jump.

And if possible, optimized to only be interested in jumps generated
immediately within the interesting scope, to filter out jumps generated
at deeper scopes not directly of interest.

/** runs the given closure five times for each element */
public void fiveForEach(Closure c) {
int elementIndex = 0;

while (elementIndex < size) {
int loopCount = 0;

loop { // loop takes a closure
if (loopCount >= 5) break; // return ends the inner loop

c.call(getElement(elementIndex));
loopCount++;
}
elementCount++;
}
}
...
public Object weirdFind() {
x = createSomeList();
x.fiveForEach {(Object o) if (someTest(o)) return o; }
}

Here, the run method is only interested in a ReturnJump, and more
specifically, it's only interested in the ReturnJump that would be
generated within the closure passed to fiveForEach. The loop inside
fiveForEach is only interested in the BreakJump generated inside the
loop. However, because the ReturnJump is generated by a call to that
closure inside the loop, it will pass through the loop's
jump/exception-handling table. Rather than have all closure-accepting
methods catch and filter all jumps, it would be preferable that they
only catch the ones they could reasonably respond to.

Of course, it should still be possible for someone to write code that
explicitly catches Jump, to avoid a rogue (i.e. badly compiled?) jump
event from blowing the whole stack. This could also be a recommended
documentation practice:

will capture any return or break jumps passing through it:
/**
* ...
* @jumps ReturnJump, BreakJump
*/

...

will capture all jumps; don't expect jumps passing through this to escape
/**
* ...
* @jumps *

Unnecessary perhaps?

- Charlie

Alex Tkachman

unread,
Oct 20, 2007, 3:44:46 PM10/20/07
to jvm-la...@googlegroups.com
If I understand correctly for jumps we can get rid of filling stack
trace, so it will be more or less effective.

On 10/20/07, Charles Oliver Nutter <charles...@sun.com> wrote:
>

Neal Gafter

unread,
Oct 20, 2007, 8:43:06 PM10/20/07
to jvm-la...@googlegroups.com
On 10/20/07, Charles Oliver Nutter <charles...@sun.com> wrote:
Neil Gafter: I recall you advocating non-local returns, but I don't
remember if you also advocate other types of non-local flow control like
break or continue. Do you?

Yes, but the way it is specified in http://www.javac.info, you'd want to declare and invoke the "each" method with the "for" qualifier, otherwise it isn't a matching target for the break. The invocation syntax would then be

for myList.each(String element :) {

    if (element.equals("foo")) break;
}

for a member method, or

for each(String element: myList) {
    if (element.equals ("foo")) break;
}

for a static method. I'm working separately on a spec/implementation of extension methods, which roughly speaking would allow you to use these forms interchangeably. In other words, to take advantage of this you wouldn't have to make changes to the Collection interface (interface changes break backward compatibility in Java).  


Mark Derricutt

unread,
Oct 20, 2007, 8:56:57 PM10/20/07
to jvm-la...@googlegroups.com
In this work your doing are there any provisions for determining the current position in the loop?  i.e. is there anything coming after me?

An example from some code I was just fixing up was taking a list of class names and combining them into a ;  delimited string (and erroneously appending a trailing separator), without knowing the current position, or if something follows you one has to manually keep track of position to optionally append a ; rather than just call say ' iterator.hasNext()" inside the loop.

With the syntax below, it would be nice if a new implicit variable was available which gave up such info, maybe something like:

StringBuilder sb = new StringBuilder();
for each(String className: myListOfClassNames) {
  sb.append(className);
  if (each.hasNext()) sb.append(";");
}

with each being a new implicit variable of some mythical Iteration interface (this might also provide a getIndex() method to identify which iteration of the loop were in).

Having access to this information is one of the things I miss with the new enhanced for loops...


On 10/21/07, Neal Gafter < neal....@gmail.com> wrote:

for each(String element: myList) {
    if (element.equals ("foo")) break;
}

for a static method. I'm working separately on a spec/implementation of extension methods, which roughly speaking would allow you to use these forms interchangeably. In other words, to take advantage of this you wouldn't have to make changes to the Collection interface (interface changes break backward compatibility in Java).  


--
It`s not the tree that forsakes the flower, but the flower that forsakes the tree
Someday I`ll learn to love these scars - Bye Bye Beautiful

hlovatt

unread,
Oct 20, 2007, 9:18:20 PM10/20/07
to JVM Languages
Is it really necessary to have a new feature. I would of thought a
convention that you don't catch something derived from Jump was
sufficient. Much like you are highly advised not to catch something
derived from Error at the moment. I say this because inner classes
give sufficient power to make non-local jumps fast at present, e.g.:

class Jump extends RuntimeException { ... }

class NonLocalReturn extends Jump {
private Object value;
Object getValue() { ... }
void setValue() { ... }
}

interface Method1&lt;R, A> { R call( A a ); }

class MyList&lt;E> {
&lt;R> MyList&lt;R> map( Method&lt;R, E> f ) { ... }
// rest
}

// Translated from a better syntax in to standard Java
Integer example( MyList&lt;Integer> list ) {
class ExampleReturn extends NonLocalReturn {
static final ExampleReturn instance = new ExampleReturn(); // Pre-
made singleton
}
try {
...
list.map( new Method1&lt;Integer, Integer>() {
public Integer call( Integer x ) {
...
throw ExampleReturn.instance; // fast - pre made
}
} );
...
} catch ( ExampleReturn e ) {
return (Integer) e.getValue();
}
}
}

Neal Gafter

unread,
Oct 20, 2007, 10:40:37 PM10/20/07
to jvm-la...@googlegroups.com
On 10/20/07, Mark Derricutt <ma...@talios.com> wrote:
In this work your doing are there any provisions for determining the current position in the loop?  i.e. is there anything coming after me?

An example from some code I was just fixing up was taking a list of class names and combining them into a ;  delimited string (and erroneously appending a trailing separator), without knowing the current position, or if something follows you one has to manually keep track of position to optionally append a ; rather than just call say ' iterator.hasNext()" inside the loop.

With the syntax below, it would be nice if a new implicit variable was available which gave up such info...

These loops are library methods, not separate looping language extensions.  Closures just make it possible for programmers to define them. One can easily make a looping method that has an index or that exposes an iterator.


On 10/20/07, hlovatt < howard...@gmail.com> wrote:
Is it really necessary to have a new feature. I would of thought a
convention that you don't catch something derived from Jump was
sufficient.

A new exception hierarchy outside Throwable is necessary for closures to satisfy Tennent's Correspondence Principle, which as you know is a powerful litmus test for the expressive power of a language feature such as closures.

Charles Oliver Nutter

unread,
Oct 21, 2007, 8:41:25 PM10/21/07
to jvm-la...@googlegroups.com
Alex Tkachman wrote:
> If I understand correctly for jumps we can get rid of filling stack
> trace, so it will be more or less effective.

That's how our "Jump" type in JRuby works: we override fillInStackTrace
to do nothing. In cases where there's no mutable data on the jump, we
also use a single object instance everywhere.

- Charlie

Charles Oliver Nutter

unread,
Oct 21, 2007, 8:44:56 PM10/21/07
to jvm-la...@googlegroups.com
hlovatt wrote:
> Is it really necessary to have a new feature. I would of thought a
> convention that you don't catch something derived from Jump was
> sufficient. Much like you are highly advised not to catch something
> derived from Error at the moment. I say this because inner classes
> give sufficient power to make non-local jumps fast at present, e.g.:

The problem is that there's already code out there that catches
Exception or Throwable, which will break any language implementation
using RuntimeException subclasses to do non-local flow control. Scala
opted to go with Error subclasses, but even here there's the possibility
someone could be catching Error for other reasons. The fact that Jump
would be new and that there would be no other valid reason for catching
it other than intercepting non-local flow control is exactly the point.
We're not overloading an existing feature in ways incompatible with
current usage patterns. We're accepting that non-local flow control is a
fact of life for many language implementations, and that a first-class
construct is needed.

- Charlie

hlovatt

unread,
Oct 21, 2007, 8:54:20 PM10/21/07
to JVM Languages
On Oct 21, 12:40 pm, "Neal Gafter" <neal.gaf...@gmail.com> wrote:
> A new exception hierarchy outside Throwable is necessary for closures to
> satisfy Tennent's Correspondence Principle, which as you know is a powerful
> litmus test for the expressive power of a language feature such as closures.

Tennent used his Principle of Correspondence to argue against return,
break, and continue and instead advocated that every block should have
one exit point (end of block). His argument in Javanese is:

// Start with this block - the block is between the braces
int i = 0;
while ( true ) {
if ( i > 3 ) break;
out.println( i );
}

Then wrap the block in another block:

// Using Correspondence it should be the same - but it isn't
int i = 0;
while ( true ) {
while ( true ) {
if ( i > 3 ) break;
out.println( i );
}
}

Oops meaning of block changed, contrast this with:

int i = 0;
while ( true ) {
if ( i <= 3 ) out.println( i );
}

Then wrap the block in another loop - the block now has the same
result:

int i = 0;
while ( true ) {
while ( true ) {
if ( i <= 3 ) out.println( i );
}
}

Therefore using Correspondence as an argument for a JVM extension to
support return etc. from within closures is weak, since Tennent
argued, using his own principle, that the correct answer is not to
have return etc.

A compromise position is to allow return etc. but require them to name
the block they are returning from (like break and continue currently
do but also extend the naming to include method names). Then at least
the action of the block is clearly flagged, e.g.:

int i = 0;
iLoop: while ( true ) {
while ( true ) {
if ( i > 3 ) break iLoop; // still works as expected
out.println( i );
}
}

Having said that the block must be named, then the standard exception
mechanism can be used and this has the added advantage of providing a
compile time check for asynchronous use cases, e.g.:

// in the example below method { .. } is suggested short syntax for an
inner class (closure)
int i = 0;
iLoop: while ( true ) {
invokeLater( method { ...; break iLoop; ... } ); // Error since
invokeLater doesn't catch iLoopBreakException
}
}

The above example is caught at compile time because when expanded the
object passed to invokeLater throws a checked exception, iLoopBreak,
which isn't caught (and can't be caught because it is a local class)
by invokeLater. See previous return example for how break etc. are
expanded.

I would also caution that adding support for non-local returns will
probably not be worth the trouble, since I doubt that non-local
returns will be used much. For example, what is a good use case
(remember that many languages don't have return etc. at all)? A second
question, how often do you use a named break or continue? You can
always add features but a more pertinent question is whether a feature
is worth adding, not whether it is technically feasible to add. There
is a ledger here, you need to account cost of addition against
usefulness.

NOTE: There is a typo in my original post, exception Jump is meant to
extend Exception not RuntimeException, IE it is meant to be a checked
exception so that asynchronous use cases are caught at compile time.
Also note that the exception, ExampleReturn, is an inner class and
therefore private to the method, example, in which the inner class
(closure) is declared in and therefore cannot be caught by anything
else (except for catching Jump which like Error it is suggested that
you don't do).

hlovatt

unread,
Oct 21, 2007, 9:22:46 PM10/21/07
to JVM Languages
On Oct 22, 10:44 am, Charles Oliver Nutter <charles.nut...@sun.com>
wrote:

> The problem is that there's already code out there that catches
> Exception or Throwable, which will break any language implementation
> using RuntimeException subclasses to do non-local flow control. Scala
> opted to go with Error subclasses, but even here there's the possibility
> someone could be catching Error for other reasons. The fact that Jump
> would be new and that there would be no other valid reason for catching
> it other than intercepting non-local flow control is exactly the point.
> We're not overloading an existing feature in ways incompatible with
> current usage patterns. We're accepting that non-local flow control is a
> fact of life for many language implementations, and that a first-class
> construct is needed.

OK these are valid points. So why not make a new subclass of Throwable
called Jump that acts like a checked exception, Exception, in Java and
recommend that people don't catch it (but importantly allow them to
catch it - see below). I doubt that any mechanism is totally bullet
proof, so I don't see a problem with a simple recommendation in the
Java context. For other languages it may be appropriate to enforce
that Jump cannot be caught in that language. I am deliberately putting
forward a Java and a non-Java viewpoint since in the ideal world
multiple languages can co-exist and call each other and therefore
control flow needs to have identical semantics in all cases. If it is
a specific non-Java feature then a method that uses it cannot be
called from Java, if on the other hand it is like a checked exception
then Java can call it (and catch or throw Jump if that is what it
takes to deal with the non-Java method).

Neal Gafter

unread,
Oct 21, 2007, 9:46:55 PM10/21/07
to jvm-la...@googlegroups.com
On 10/21/07, hlovatt <howard...@gmail.com > wrote:
Therefore using Correspondence as an argument for a JVM extension to
support return etc. from within closures is weak, since Tennent
argued, using his own principle, that the correct answer is not to
have return etc.

I don't think removing return from the Java Programming Language is an option. It is best to introduce as few new violations of the principle as possible. Two wrongs don't make a right.

A compromise position is to allow return etc. but require them to name
the block they are returning from (like break and continue currently
do but also extend the naming to include method names). Then at least
the action of the block is clearly flagged, e.g.:

We are already looking at allowing labelled closure returns in the Java Closures spec (and have a draft spec for it). Nevertheless, as a separate issue exceptions outside Throwable are still needed.

Charles Oliver Nutter

unread,
Oct 21, 2007, 9:49:48 PM10/21/07
to jvm-la...@googlegroups.com
hlovatt wrote:
> OK these are valid points. So why not make a new subclass of Throwable
> called Jump that acts like a checked exception, Exception, in Java and
> recommend that people don't catch it (but importantly allow them to
> catch it - see below). I doubt that any mechanism is totally bullet
> proof, so I don't see a problem with a simple recommendation in the
> Java context. For other languages it may be appropriate to enforce
> that Jump cannot be caught in that language. I am deliberately putting
> forward a Java and a non-Java viewpoint since in the ideal world
> multiple languages can co-exist and call each other and therefore
> control flow needs to have identical semantics in all cases. If it is
> a specific non-Java feature then a method that uses it cannot be
> called from Java, if on the other hand it is like a checked exception
> then Java can call it (and catch or throw Jump if that is what it
> takes to deal with the non-Java method).

Full disclosure: I'm rapidly losing respect for checked exceptions.

That said, I think making Jump a checked exception is a *terrible* way
to go. Not only would it require adding the checked exception to every
single API we want to closure-enable, it requires that anywhere we want
to have non-local flow control in play we have to explicitly declare it.
Even worse, it requires that all intermediate APIs that may not even
care about non-local flow control would also have to declare they throw
jumps even if they're just passing a closure on to a deeper method.

// foo does the actual call...
void foo(Closure c1) {
c1.call();
}

// ...but bar, simply by virtue of accepting a closure, would have to
// declare that it throws Jump
void bar(Closure c2) {
foo(c2);
}

Essentially, it would require that all methods dealing with closures
must throw Jump, even if the closures they're passed never use non-local
flow control, simply because *they might*.

I think the detail you're missing here is that non-local flow control is
not a feature of the closure-friendly API you are calling, it's a
feature of the closure you're passing to it. Therefore it makes no sense
to require that all closure-accepting methods throw Jump, since in many
(most?) cases they won't, nor will they typically care if non-local
Jumps pass through them.

- Charlie

hlovatt

unread,
Oct 22, 2007, 1:04:43 AM10/22/07
to JVM Languages
On Oct 22, 11:46 am, "Neal Gafter" <neal.gaf...@gmail.com> wrote:
> We are already looking at allowing labelled closure returns in the Java
> Closures spec (and have a draft spec for it).

I am glad to hear that you are considering this, I think it is the way
to go. (As you know I have advocated named returns etc. for some time,
EG http://www.artima.com/weblogs/viewpost.jsp?thread=182412)

Now all I need to do is to convince you to base your closures on
normal inner classes <- this comment is meant in jest, it isn't a dig
at you

> Nevertheless, as a separate
> issue exceptions outside Throwable are still needed.

I agree that there are some advantages, but question whether it is
really worth the trouble (i.e. the cost penalty ratio doesn't seem
that good to me).

hlovatt

unread,
Oct 22, 2007, 1:26:29 AM10/22/07
to JVM Languages
On Oct 22, 11:49 am, Charles Oliver Nutter <charles.nut...@sun.com>
wrote:

> Essentially, it would require that all methods dealing with closures
> must throw Jump, even if the closures they're passed never use non-local
> flow control, simply because *they might*.
>
> I think the detail you're missing here is that non-local flow control is
> not a feature of the closure-friendly API you are calling, it's a
> feature of the closure you're passing to it. Therefore it makes no sense
> to require that all closure-accepting methods throw Jump, since in many
> (most?) cases they won't, nor will they typically care if non-local
> Jumps pass through them.

Maybe, you could choose to make Jump exceptions non-checked. But as a
counter example consider multi-threaded code or event code where it
makes no sense to have a non-local Jump. In these cases you want the
compiler to tell you that you have made a mistake. It is really a
matter of what you consider most important. I write quite a bit of
event and multi-threaded code and therefore like catching this error.

My proposal for named non-local jumps, http://www.artima.com/weblogs/viewpost.jsp?thread=182412,
did include a mechanism for dealing with both cases simply. The
proposal also extends Java to accept a list, possibly empty, of
exceptions that can be thrown as a generic-type-var-arg. Thus if an
inner class (closure) threw no exceptions then it could be passed to a
method that didn't deal with exceptions, conversely if it threw a
checked exception, Jump in this context, then a method it was passed
to would need to deal with that exception or declare the exception
itself.

The main point is that we don't really need to extend the JVM, the
current exception mechanism seems sufficient.

Neal Gafter

unread,
Oct 22, 2007, 1:54:33 AM10/22/07
to jvm-la...@googlegroups.com
On 10/21/07, hlovatt <howard...@gmail.com> wrote:
I agree that there are some advantages, but question whether it is
really worth the trouble (i.e. the cost penalty ratio doesn't seem
that good to me).

The costs are ones that only those of us who implement languages pay.  The benefits are accrued by all those who use the language. Seems like a pretty good tradeoff for our users.

Geert Bevin

unread,
Oct 22, 2007, 3:36:02 AM10/22/07
to jvm-la...@googlegroups.com
Hi Charles,

this is a very good idea and I can see no downsides. Those exception
types could also automatically be setup to not create a stack trace
(which saves a lot of overhead). I've been using Error exception for
this myself (seems Scala does too), but a dedicated type that nobody
is catching would be a good improvement.

Geert

--
Geert Bevin
Terracotta - http://www.terracotta.org
Uwyn "Use what you need" - http://uwyn.com
RIFE Java application framework - http://rifers.org
Music and words - http://gbevin.com

Jochen Theodorou

unread,
Oct 27, 2007, 6:38:04 PM10/27/07
to jvm-la...@googlegroups.com
hlovatt schrieb:

> On Oct 21, 12:40 pm, "Neal Gafter" <neal.gaf...@gmail.com> wrote:
>> A new exception hierarchy outside Throwable is necessary for closures to
>> satisfy Tennent's Correspondence Principle, which as you know is a powerful
>> litmus test for the expressive power of a language feature such as closures.
>
> Tennent used his Principle of Correspondence to argue against return,
> break, and continue and instead advocated that every block should have
> one exit point (end of block).

sorry for warming up this thread after a week...
It is a shame that I not yet had a chance of reading this... but maybe
you can answer some questions.

For example how did he define exit point? for example in this case

def foo() { throw new RuntimeException("something") }

while (true) {
foo()
}

you said "end of block", so I guess it has only one exit point... or
does the foo() method have two then? If yes, wouldn't mean that
exceptions are bad in Tennent's Correspondence Principle?

But ok, let us say this has only one exit point.... Now, if we have a
list and we want to proceed all elements until we meet a certain element
and then stop processing, then I would write something like this:

for (XY element : list) {
if (element==myBreakCondition) break
doSometing(element)
}

this has (at last) two exit point because of the loop, so I guess I
would have to change the loop condition to do the additional check

XY element
for (Iterator it=list.iterator();
it.hasNext() && element!=myBreakCondition;) {
element = it.next()
doSomething(element)
}

but in case of a "closure" I could do this:

list.each { XY element, LoopFlow flowControl ->
flowControl.breakIf(element==myBreakCondition)
doSometing(element)
}

where flowControl.breakIf would throw an exception, each (I will call
each an iteration method, because it iterates over the list here) would
catch that exception and use flowControl to check the exception... Would
this still fulfill the "only one exit point per block" idea?

Or the next question... if we say that a "closure syntax" overlaps with
the syntax generally used by blocks, but if the "closure" is no block...
does the principle then still apply to break/continue which have to be
applied to the iteration method?

Or let us say a break statement is not allowed in a "closure".. what
does the principle say to this? I mean having one exit point per block
is easily fulfilled with that, because it would not compile... Is the
language to be considered as "less expressive" then, even if the break
statement can be expressed by other elements that are not of syntactic
nature? Or if I had a different syntactic element to do a break in a
closure.. would the language then still be considered as "less powerful"?

[...]


> Having said that the block must be named, then the standard exception
> mechanism can be used and this has the added advantage of providing a
> compile time check for asynchronous use cases, e.g.:
>
> // in the example below method { .. } is suggested short syntax for an
> inner class (closure)
> int i = 0;
> iLoop: while ( true ) {
> invokeLater( method { ...; break iLoop; ... } ); // Error since
> invokeLater doesn't catch iLoopBreakException
> }
> }
>
> The above example is caught at compile time because when expanded the
> object passed to invokeLater throws a checked exception, iLoopBreak,
> which isn't caught (and can't be caught because it is a local class)
> by invokeLater. See previous return example for how break etc. are
> expanded.

on the other hand.... why is that a illegal usage of "break iLoop"? I
mean ok, invokeLater might execute the "closure" in a different Thread,
but what if not?

I guess that's why the closure proposal expects the usage of "for" here:

l1: while (true) {
l2: for each(element:list) {
if (element == myBreakCondition) break l1
break l2
}
}

from a pragmatic point of view I guess you would have to tell your
iteration method that it needs to respond to "break l2", but not to
"break l1". But of course I can not write:

void for each(List list, MyClosure closure, Label label) {
label: for (x : list) closure.call()
}

and you can get even more fun with something like:

l1: while (true) {
l2: for each(element:list) {
check (element == myBreakCondition, {break l1})
break l2
}
}

which means that check should execute the closure {break l1} if
element==myBreakCondition. If we use the interface coercion that was
proposed, then we would have to throw an compile time error here if the
interface (for example Runnable) does not throw that exception. Or not?
I mean according to your text above it must, because Runnable does not
throw that exception and check would have to throw that exception too.
But the advantage of the "for" keyword usage seems to be kind of lost here.

So from looking at this from the perspective of a static type system
where I want to have compile time checks as much as possible a checked
exception would make sense to me. From the perspective of a API writer I
get horrified but that ideas, because it means any method that somehow
has to handle "closures" containing a break/continue would have to throw
the exception. It would be equally annoying to he RemoteException for
example. On the bytecode level it makes no difference, because the JVM
is not checking if a exception is checked or not, only the Java compiler
does.

Regardless if new exception type is defined or not, I guess the API
writers still get exposed to an implementation detail if they want to
have their custom iteration methods which I think is bad...

And even if there is a way to express the each without letting it
explicitly catch and evaluate the exception (I would really like to know
how to get around that), a checked exception would still mean, that at
some point any API writer gets exposed to this implementation detail
when they use the iteration method on their own.

> I would also caution that adding support for non-local returns will
> probably not be worth the trouble, since I doubt that non-local
> returns will be used much. For example, what is a good use case
> (remember that many languages don't have return etc. at all)?

so you have no problem with break, but with return? I hope my check
method example above did show you that it is not only "return", it is
also break/continue to some extend.

bye blackdrag

--
Jochen "blackdrag" Theodorou
Groovy Tech Lead (http://groovy.codehaus.org)
http://blackdragsview.blogspot.com/
http://www.g2one.com/

hlovatt

unread,
Oct 29, 2007, 4:17:05 PM10/29/07
to JVM Languages
Tennent's book is quite old and talks about Pascal, I don't recall any
specific discussion of exceptions and exceptions were not part of
standard Pascal. So my guess is that any form of non-local jumps would
not be covered by Tennent directly. The princiople says that the
meaning of the code should not change if enclosed in a block, e.g.
while loop, but it means any type of block and therefore also a try
catch block.

So my reading is that any form of non-block structured jump is
excluded by the principle, i.e. no return, break, continue, or throw
statements. All these have to be simulated with a status variable.
Interestingly if an inner class was used instead of a block then a
control variable could be used and exceptions avoided all together:

enum Status { CONTINUE, BREAK; }

class Loop implements Method1< Void, Object > {
Status status = CONTINUE;
void breakLoop() { status = BREAK; }
}

class List {
private Object[] data;
...
void each( Loop l ) {
for ( int i = 0; i < data.length && l.status == CONTINUE; i++ )
{ l.call( data[ i ] ); }
}
}

// My favourite syntax follows!
list.each method( e ) { if ( someCondition ) { breakLoop() } };

Jochen Theodorou

unread,
Oct 29, 2007, 7:09:56 PM10/29/07
to jvm-la...@googlegroups.com
hlovatt schrieb:

> Tennent's book is quite old and talks about Pascal, I don't recall any
> specific discussion of exceptions and exceptions were not part of
> standard Pascal. So my guess is that any form of non-local jumps would
> not be covered by Tennent directly. The princiople says that the
> meaning of the code should not change if enclosed in a block, e.g.
> while loop, but it means any type of block and therefore also a try
> catch block.

I found out it is from around 1977, another reason why it is so hard to
find... citeseer doesn't have papers that old ;) But I also think that
an exception would be counted as exit point. On the other hand
simulating an exception by control variables is not very nice.

> So my reading is that any form of non-block structured jump is
> excluded by the principle, i.e. no return, break, continue, or throw
> statements. All these have to be simulated with a status variable.
> Interestingly if an inner class was used instead of a block then a
> control variable could be used and exceptions avoided all together:
>
> enum Status { CONTINUE, BREAK; }
>
> class Loop implements Method1< Void, Object > {
> Status status = CONTINUE;
> void breakLoop() { status = BREAK; }
> }
>
> class List {
> private Object[] data;
> ...
> void each( Loop l ) {
> for ( int i = 0; i < data.length && l.status == CONTINUE; i++ )
> { l.call( data[ i ] ); }
> }
> }
>
> // My favourite syntax follows!
> list.each method( e ) { if ( someCondition ) { breakLoop() } };

but you are not showing that the call to breakLoop() has to be at a
position where no further commands follow. so if you have

while (true) {
println "1"
if (b) break;
println "2"
}

you would have to split it in a b==true and b!=true part:

while (doLoop) {
if (b) {
println "1"
doLoop=false
} else {
println "1"
println "2"
}
}

which might have less exit points, but is also less readable in my eyes.
All in all it seems I will be no fan of Tennent's idea. In my last post
I used something compareable to your breakLoop() command, but I had in
mind that it throws an exception, that is cached before the block is
left and then a iterator method from outside could handle that.... in
your case, instead of doing a simple l.call you would have to put a
try-catch around to be able to skip parts of the contents of your Loop
subclass.


bye blackdrag

--
Jochen "blackdrag" Theodorou

Neal Gafter

unread,
Oct 29, 2007, 7:25:25 PM10/29/07
to jvm-la...@googlegroups.com
On 10/29/07, hlovatt <howard...@gmail.com > wrote:
Tennent's book is quite old and talks about Pascal, I don't recall any
specific discussion of exceptions and exceptions were not part of
standard Pascal. So my guess is that any form of non-local jumps would
not be covered by Tennent directly. The princiople says that the
meaning of the code should not change if enclosed in a block, e.g.
while loop, but it means any type of block and therefore also a try
catch block.

That is not Tennent's Correspondence Principle.

So my reading is that any form of non-block structured jump is
excluded by the principle, i.e. no return, break, continue, or throw
statements. All these have to be simulated with a status variable.

Your reasoning isn't valid (that Tennent applied his principles to analyze Pascal, and Pascal doesn't have multiple exit points, and therefore Tenent's principles don't support multiple exit points).

Interestingly if an inner class was used instead of a block then a
control variable could be used and exceptions avoided all together:

If assembly language were used instead of a high-level language, one could avoid blocks, inner classes, exceptions, and variables too!

Jochen Theodorou

unread,
Oct 29, 2007, 7:46:56 PM10/29/07
to jvm-la...@googlegroups.com
Neal Gafter schrieb:
> On 10/29/07, *hlovatt* <howard...@gmail.com
> <mailto:howard...@gmail.com>> wrote:
>
> Tennent's book is quite old and talks about Pascal, I don't recall any
> specific discussion of exceptions and exceptions were not part of
> standard Pascal. So my guess is that any form of non-local jumps would
> not be covered by Tennent directly. The princiople says that the
> meaning of the code should not change if enclosed in a block, e.g.
> while loop, but it means any type of block and therefore also a try
> catch block.
>
> That is not Tennent's Correspondence Principle.

and without having the ability to read it... I guess I need to find a way :(

> So my reading is that any form of non-block structured jump is
> excluded by the principle, i.e. no return, break, continue, or throw
> statements. All these have to be simulated with a status variable.
>
> Your reasoning isn't valid (that Tennent applied his principles to
> analyze Pascal, and Pascal doesn't have multiple exit points, and
> therefore Tenent's principles don't support multiple exit points).

I did read his text as that Tennent was against using multiple exit
points, not that Pascal has no exit points like return/break/continue.
Pascal has no exceptions... well at last the versions of Pascal I worked
with.

bye blackdrag

--
Jochen "blackdrag" Theodorou

John Cowan

unread,
Oct 29, 2007, 7:54:46 PM10/29/07
to jvm-la...@googlegroups.com
On 10/29/07, Jochen Theodorou <blac...@gmx.org> wrote:

> I did read his text as that Tennent was against using multiple exit
> points, not that Pascal has no exit points like return/break/continue.

In fact, though, there are no such non-local exits in Standard Pascal.
You can simulate a local exit with "goto", and people used to do
that to implement "return".

--
GMail doesn't have rotating .sigs, but you can see mine at
http://www.ccil.org/~cowan/signatures

Jochen Theodorou

unread,
Oct 29, 2007, 8:09:23 PM10/29/07
to jvm-la...@googlegroups.com
John Cowan schrieb:

> On 10/29/07, Jochen Theodorou <blac...@gmx.org> wrote:
>
>> I did read his text as that Tennent was against using multiple exit
>> points, not that Pascal has no exit points like return/break/continue.
>
> In fact, though, there are no such non-local exits in Standard Pascal.
> You can simulate a local exit with "goto", and people used to do
> that to implement "return".

ah, yes, I begin to remember... and I think I remember, that there was
no return too... instead you needed to have a function and assign the
return value to the function name... something like that.

John Cowan

unread,
Oct 29, 2007, 8:12:54 PM10/29/07
to jvm-la...@googlegroups.com
Just so.

function plustwo (i : integer) : integer
begin
plustwo := i + 2
end

hlovatt

unread,
Oct 30, 2007, 5:25:05 PM10/30/07
to JVM Languages
Hi Jochen,

I agree with all you say including that Tennent's principle isn't
necessarily the best way to design a language. An alternative that I
find better is to be pure OO and say everything is an instance of a
class. This isn't Java at present, i.e. Java has blocks and
primitives. For new features though it would be nice if everything was
an object. This would make refactoring easier, e.g. lifting a block to
a method, e.g. your block example:

while ( true ) {
out.println( "1" );
if ( b ) break;
out.println( "2" );
}

Would, if blocks were methods, become:

while ( true ) {
out.println( "1" );
if ( b ) return BREAK;
out.println( "2" );
}

IE the stuff between {} is a method, so return BREAK behaves like
break and the advantage is that you can refactor to:

while ( true ) { method( b ); }

Status method( boolean b ) {
out.println( "1" );
if ( b ) return BREAK;
out.println( "2" );
}

Then onto:

while ( true ) { example.method( b ); }

class Example {
Status method( boolean b ) {
out.println( "1" );
if ( b ) return BREAK;
out.println( "2" );
}
}

Anyway this is rather off topic, but I am with you that Tennent's
principle may not be the best choice.

Cheers,

Howard.

Neal Gafter

unread,
Oct 30, 2007, 5:49:08 PM10/30/07
to jvm-la...@googlegroups.com
On 10/30/07, hlovatt <howard...@gmail.com> wrote:
I agree with all you say including that Tennent's principle isn't
necessarily the best way to design a language.

I think he was referring to your interpretation of Tennent's principle. I also agree that your interpretation of Tennent's principles aren't useful in language design.

hlovatt

unread,
Oct 30, 2007, 6:43:30 PM10/30/07
to JVM Languages
Hi Neal,

Tennent in his book does directly reject return, break, and continue
using his principle. I speculate that he would also reject exceptions,
but I don't recall him discussing exceptions directly. Tennent rejects
break (and continue and return) on two grounds:

1. The following, break example, is a syntax error outside of a
looping block

break;

Therefore

while ( true ) { break; }

Should also be an error, i.e. enclosing in a block has changed the
meaning of the code (in this case drastically from error to valid).
Note the principle also says you should be able to remove a block, not
just add it.

2. The following, return example, changes meaning when enclosed in a
method (Pascal had inner functions and Tennent primarily used inner
functions to demonstrate his principle - below is in Javanese where
inner methods are allowed)

int outer() { return 1; }

Chances meaning (becomes an error) when enclosed in an inner method:

int outer() {
void inner() { return 1; }
inner();
}

Contrast this with:

int outer() {
outer = 1; // no return statement, Pascal uses method name for
return values
}

Now it doesn't change meaning when enclosed in an inner method:

int outer() {
void inner() { outer = 1; }
inner();
}

Tennent primarily used enclosing statements in inner functions for his
arguments, so the second argument presented above is a typical Tennent
argument (but please forgive the extension to Java for the inner
methods). The key from the second Tennent example is that a name is
given for what you are returning from. So you could use named returns:

int outer() {
void inner() { outer.return 1; }
inner();
}

By the way I don't think that Tennent's principle is always
applicable, for example I like return statements and hence my
suggestion of named returns.

To get back on topic; my point is that using Tennent's principle to
justify adding features to the JVM to support return etc. is a weak
argument because Tennent said you shouldn't have return etc. in the
first place! I am saying that if the only argument for a JVM extension
is based on return etc. and Tennent then I think the JVM should not be
extended.

Cheers,

Howard.

On Oct 30, 10:25 am, "Neal Gafter" <neal.gaf...@gmail.com> wrote:

Reply all
Reply to author
Forward
0 new messages