PROPOSAL: Enhanced for each loop iteration control

Ruslan Shevchenko Ruslan at Shevchenko.Kiev.UA
Tue Apr 21 22:23:22 PDT 2009


> Catching up on proposal commenting, while I find the design generally
> acceptable, I remain unconvinced that the inability to replace all
> pre-JDK 5 for loops with some newer, improved for loop is a serious
> enough problem to justify further language changes beyond the enhanced
> for loop in JDK 5.
>
> Some data on how often such a new construct would be applicable would
> help here.
>

 Partially it was RemoveInLoop (which count access to remove within while
operator) in
http://mail.openjdk.java.net/pipermail/coin-dev/2009-April/001496.html
Number was quite low (for example 62 in JDK)

I will try to add count for remove-in-for and index-access during next
iteration in my set of coin syntax-patterns. (during next week-end if we
have no time constraints)


> -Joe
>
> 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