The magic behind foreach (was: Re: Descent 0.5.3 released)

Ary Borenszweig ary at esperanto.org.ar
Wed Jan 21 22:30:15 PST 2009


Ary Borenszweig wrote:
 > BCS wrote:
 >> Reply to Robert,
 >>
 >>> That doesn't look entirely useless, especially for optimization.
 >>> Perhaps hard to read, but easier than reading the assembly output ;-P!
 >>>
 >>
 >> ditto; now that you have it might as well make it available.
 >
 > Ok, I'll work on it. :-)

I still have to work on some stuff, but...

Before:
---
module main;

import std.stdio;

class Foo {
	uint array[2];

	int opApply(int delegate(ref uint) dg) {
		int result = 0;

		for(int i = 0; i < array.length; i++) {
			result = dg(array[i]);
			if(result)
				break;
		}
		return result;
	}
}

int main(char[][] args) {
	Foo foo = new Foo();
	foreach(x; foo) {
		if (x == 3) {
			break;
		}
		writefln("%s", x);
	}
	return 0;
}
---

After:
---
module main;

import object;
import std.stdio;

class Foo: Object {
	uint[2] array;

	int opApply(int delegate(ref uint) dg) {
		assert(this, "null this");
		{
			int result = 0;
			for(int i = 0; cast(uint) i < 2; i++) {
				result = dg(this.array[cast(uint) i]);
				if(result)
					break;
			}
			return result;
		}
	}
}

int main(char[][] args) {
	Foo foo = new Foo;
	foo.opApply(delegate (uint __applyArg0) {
		{
			{
				uint x = __applyArg0;
				if(x == 3)
					return 1;
				writefln("%s", x);
			}
			return 0;
		}
	} );
	return 0;
}
---

Ummm... I was wondering... In every implemetation of opApply, after you 
invoke the delegate you must check the result to see if it's non zero, 
right? In that case, you must break the iteration.

If the compiler can transform a "foreach" into an opApply call, passing 
the foreach body and converting breaks to "return 1" statements... can't 
opApply be specified as:

int opApply(void delegate(ref uint) dg) { // note: delegate returns void
}

and the compiler transforms the opApply signature to the one that's used 
now, plus converting each dg call to a call and a check of return value 
  != 0 and return 1 in that case?

Yes, yes, this is not trivial at all, but it's possible. And then D 
programmers no longer have to make ifs and return magic numbers to make 
foreach work. (think: do once in the compiler, eliminate thousands of 
boilerplate codes in programs)

So basically:

---
int opApply(void delegate(ref uint) dg) {
   for(int i = 0; i < array.length; i++) {
     dg(array[i]);
}
---

would be converted to:


---
int opApply(void delegate(ref uint) dg) {
   for(int i = 0; i < array.length; i++) {
     result = dg(array[i]);
     if (result) return 1;
}
---

What do you think?

(By the way, why opApply returns an int? What's the use of that?)



More information about the Digitalmars-d mailing list