PROPOSAL: Enhanced for each loop iteration control

Stephen Colebourne scolebourne at joda.org
Sat Mar 28 03:40:19 PDT 2009


Were there any comments on this?
Stephen

Stephen Colebourne wrote:
> Enhanced for each loop iteration control
> 
> (re-sent with correct subject line)
> 
> This proposal extends the for each loop to allow access to meta data
> including the index and the remove method.
> 
> (This proposal doesn't really go into enough detail - with JSR-310 my
> time here is limited. I'd hope that there is just about enough though....)
> 
> -----------------------------------------------------------------------------------
> Enhanced for each loop iteration control
> 
> AUTHOR(S):
> Stephen Colebourne
> 
> *OVERVIEW*
> 
> FEATURE SUMMARY:
> Extends the Java 5 for-each loop to allow access to the loop index,
> whether this is the first or last iteration, and to remove the current item.
> 
> MAJOR ADVANTAGE:
> The for-each loop is almost certainly the most new popular feature from
> Java 5. It works because it increases the abstraction level - instead of
> having to express the low-level details of how to loop around a list or
> array (with an index or iterator), the developer simply states that they
> want to loop and the language takes care of the rest. However, all the
> benefit is lost as soon as the developer needs to access the index or to
> remove an item.
> 
> The original Java 5 for each work took a relatively conservative stance
> on a number of issues aiming to tackle the 80% case. However, loops are
> such a common form in coding that the remaining 20% that was not tackled
> represents a significant body of code.
> 
> The process of converting the loop back from the for each to be index or
> iterator based is painful. This is because the old loop style if
> significantly lower-level, more verbose and less clear. It is also
> painful as most IDEs don't support this kind of 'de-refactoring'.
> 
> MAJOR BENEFIT:
> A common coding idiom is expressed at a higher abstraction than at
> present. This aids readability and clarity.
> 
> Accessing the index currently requires using an int based loop, or
> placing a separate int counter outside the loop (which then remains in
> scope after the loop). The proposed solution doesn't result in manual
> manipulation of the index.
> 
> Accessing the iterator remove requires using an iterator based loop.
> With generics this is remarkably verbose. The proposed solution is
> significantly shorter and cleaner.
> 
> MAJOR DISADVANTAGE:
> The enhanced for each loop is complicated with additional functionality.
> (This is mitigated by being easy and obvious to use)
> 
> More code is generated magically by the compiler. (This is mitigated by
> a simple desugaring)
> 
> ALTERNATIVES:
> Use the existing language constructs, typically the standard for loop.
> 
> Use BGGA/JCA style closures, with control statements. It should be noted
> that these are consistently the most controversial parts of the closure
> debate, making the 'let's wait for closures' argument against this
> proposal weaker (as any final closures implementation may not include
> control statements).
> 
> 
> *EXAMPLES*
> 
> SIMPLE EXAMPLE:
> 
>     StringBuilder buf = new StringBuilder();
>     for (String str : list : it) {
>       if (str == null) {
>         it.remove();
>       }
>     }
> 
> whereas, today we write:
> 
>     StringBuilder buf = new StringBuilder();
>     for (Iterator<String> it = list.iterator(); it.hasNext();) {
>       String str = it.next();
>       if (str == null) {
>         it.remove();
>       }
>     }
> 
> ADVANCED EXAMPLE:
> 
> Example1:
> 
>     for (String str : list : it) {
>       System.out.println("Row " + it.index() + " has the value " + str);
>     }
> 
> whereas, today we might write:
> 
>     int index = 0;
>     for (String str : list) {
>       System.out.println("Row " + index + " has the value " + str);
>       index++;
>     }
> 
> or
> 
>     for (int i = 0; i < list.size(); i++) {
>       String str = list.get(i);
>       System.out.println("Row " + index + " has the value " + str);
>     }
> 
> Example 2:
> 
>     StringBuilder buf = new StringBuilder();
>     for (String str : list : it) {
>       if (it.isFirst()) {
>         buf.append(str);
>       } else {
>         buf.append(", ").append(str);
>       }
>     }
> 
>     StringBuilder buf = new StringBuilder();
>     for (String str : list : it) {
>       if (it.isLast()) {
>         buf.append(str);
>       } else {
>         buf.append(str).append(", ");
>       }
>     }
> 
> 
> *DETAILS*
> 
> SPECIFICATION:
> 
> Lexical:
> No new tokens are added. The colon token is reused in the extended
> enhanced for each statement.
> 
> Syntax:
> 
>    EnhancedForStatement:
>      for ( VariableModifiersopt Type Identifier : Expression) Statement
>      for ( VariableModifiersopt Type Identifier : Expression : Ident)
> Statement
> 
> Semantics:
> 
> The first enhanced for each statement (the current form) will compile as
> it does today.
> 
> The extended enhanced for each statement will operate as follows.
> The iterator control variable is a standard variable declared to be
> final. It will never be null. The type is dependent on whether the
> expression is an array or an Iterable. It will either be
> ArrayIterationControl<T> or IterableIterationControl<T>. The type is not
> specified as it is redundent information, ie. the type is inferred. It
> is scoped for the life of the loop.
> 
> public final class IterableIterationControlIterator<T> {
>     public IterableIterationControlIterator(Iterable<T> iterable) {
>       this.control = new IterableIterationControl(iterable.iterator());
>     }
>     public boolean hasNext() { return control.hasNext() }
>     public T next() { return control.next() }
>     public IterableIterationControl<T> control() { return control }
> }
> public final class IterableIterationControl<T> {
>     public IterableIterationControl(Iterator<T> iteratorToWrap) {
>       this.it = iteratorToWrap;
>     }
>     boolean hasNext() { return it.hasNext() }
>     T next() { originalIndex++; if (lastWasRemoved) { lastWasRemoved =
> false } else { index++ } return it.next() }
>     public T remove() { removed++; lastWasRemoved = true; return
> it.remove() }
>     public int index() { return index }
>     public int originalIndex() { return originalIndex }
>     public boolean isFirst() { return index == 1 }
>     public boolean isLast() { return it.hasNext() == false }
> }
> 
> public final class ArrayIterationControlIterator<T> {
>     public ArrayIterationControlIterator(T[] array) {
>       this.control = new ArrayIterationControl(array);
>     }
>     public boolean hasNext() { return control.hasNext() }
>     public T next() { return control.next() }
>     public ArrayIterationControl<T> control() { return control }
> }
> public final class ArrayIterationControl<T> {
>     public ArrayIterationControl(T[] array) { this.array = array; }
>     boolean hasNext() { return index < array.length; }
>     T next() { return array[++index]; }
>     public int index() { return index - 1; }
>     public boolean isFirst() { return index == 1; }
>     public boolean isLast() { return index == array.length; }
> }
> 
> Exception analysis:
> The method remove() on the iteration control variable can throw an
> UnsuportedOperationException. However, this is no different from any
> other method call.
> 
> Definite assignment:
> The new variable iteration control variable is a final variable that is
> definitely assigned from creation.
> 
> COMPILATION:
> The extended enhanced for each loop is implemented by wrapping the two
> control classes around the Iterable or the array.
> 
> The Iterable design is desugared from:
> 
>     for (T item : iterable : control) { ... }
> 
> to:
>     {
>       IterableIterationControlIterator<T> $it = new
> IterableIterationControlIterator(iterable);
>       IterableIterationControl<T> control = $it.control();
>       while ($it.hasNext()) {
>         T item = $it.next();
>         ...
>       }
>     }
> 
> The array design is desugared similarly:
> 
>     {
>       ArrayIterationControlIterator<T> $it = new
> ArrayIterationControlIterator(iterable);
>       ArrayIterationControl<T> control = $it.control();
>       while ($it.hasNext()) {
>         T item = $it.next();
>         ...
>       }
>     }
> 
> There is the option to optimise this if the iteration control variable
> is not assigned to any other variable or passed to any other method.
> However, that is out of scope for now.
> 
> TESTING:
> Testing will be similar to the enhanced for loop. Arrays and Iterables
> of various types and sizes will be used. The null expression will also
> be tested.
> 
> LIBRARY SUPPORT:
> Yes, as detailed above.
> 
> REFLECTIVE APIS:
> No.
> 
> OTHER CHANGES:
> The javac tree API would need to be updated to model the change.
> 
> MIGRATION:
> Migration is not required. However, an IDE refactoring could now convert
> more int and Iterator based for loops than it does at present.
> 
> 
> *COMPATIBILITY*
> 
> BREAKING CHANGES:
> No breaking changes are known using this conversion scheme.
> 
> EXISTING PROGRAMS:
> This conversion is pure syntax sugar, so there are no known interactions
> with existing programs.
> 
> 
> *REFERENCES*
> 
> EXISTING BUGS:
> I searched the bug database, but nothing came up (which is surprising).
> 
> URL FOR PROTOTYPE:
> None
> 
> DOCUMENTS:
> [1] Stephen Colebourne's blog:
> http://www.jroller.com/scolebourne/entry/java_7_for_each_loop
> [2] Stephen Colebourne's original writeup:
> http://docs.google.com/Edit?docID=dfn5297z_15ck7x5ghr
> 
> 
> *DESIGN ISSUES*
> There are numerous alternative ways in which this feature can be added.
> These include:
> - using the keyword:
>     for (String str : list) {
>       for.remove();  // problem is nested for loops
>     }
> - using a label as a psuedo-variable:
>     it: for (String str : list) {
>       it:remove();  // note colon and not dot
>     }
> - using an additional clause before the for each colon:
>     for (String str, int index : list) {
>       // no access to remove, and conflicts for for each for maps
>     }
> 
> The chosen solution involves simple Java classes, and a simple desugar.
> The downside of the chosen solution is performance, as it involves
> creating two wrapping objects and routing hasNext() and next() via
> additional layers of method calls. A possible extension would be for the
> compiler to identify if the iteration control variable is not passed to
> another method. If so, then the code could be all be generated inline.
> 
> 
> 
> 
> 
> 



More information about the coin-dev mailing list