The magic behind foreach (was: Re: Descent 0.5.3 released)
Bill Baxter
wbaxter at gmail.com
Thu Jan 22 16:00:30 PST 2009
On Fri, Jan 23, 2009 at 8:52 AM, Ary Borenszweig <ary at esperanto.org.ar> wrote:
> Bill Baxter wrote:
>>
>> On Fri, Jan 23, 2009 at 8:10 AM, Christopher Wright <dhasenan at gmail.com>
>> wrote:
>>>
>>> Ary Borenszweig wrote:
>>>>
>>>> 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?
>>>
>>> This only fails if you wish to take a particular action when the calling
>>> code breaks out of iteration. This is not such a large use case that I
>>> think
>>> it worth preserving.
>
> Why do you mean by "fails"? The compiler transforms the foreach's body, it
> can transform the opApply's body.
>
>>
>> It's not?
>>
>> foreach(i; things) {
>> if (i==a) continue;
>> if (i==b) break;
>> if (i==d) return;
>> if (i==c) goto somewhere;
>> }
>>
>> Those are all fairly common things to do from inside the 'dg' call.
>> The int is how the compiler distinguishes which case got you out of
>> the dg.
>>
>> --bb
>
> Aaaah... Now I see what's the return value of opApply for. So I tried your
> code:
>
> (just the relevant piece)
> ---
> int main(char[][] args) {
> int a = 1, b = 2, c = 3, d = 4;
>
> Foo foo = new Foo();
> foreach(i; foo) {
> if (i==a) continue;
> if (i==b) break;
> if (i==d) return;
> if (i==c) goto somewhere;
> }
>
> somewhere:
>
> return 0;
> }
> ---
>
> and DMD spits out this:
>
> ---
> int main(char[][] args) {
> int a = 1, b = 2, c = 3, d = 4;
>
> Foo foo = new Foo;
> switch(foo.opApply(delegate (uint __applyArg0) {
> {
> uint i = __applyArg0;
> if(i == cast(uint) a)
> return 0;
> if(i == cast(uint) b)
> return 1;
> if(i == cast(uint) d)
> return 2;
> if(i == cast(uint) c)
> return 3;
> }
> return 0;
> } )) {
> default:
> break;
> case 2:
> return;
> case 3:
> goto somewhere;
> }
>
> somewhere:
>
> return 0;
> }
> ---
>
> Intersting. The compiler (Walter?) is being smart here. :-)
I posted a proposal for how to hide the magic int from the user a
while back, but my conclusion was that my approach would require AST
macros in order to give it a reasonable syntax. With the ast macros
you'd be able to do something like yield(i) in the body of your
foreach, where yield is an appropriately defined macro.
If anyone is interested I'll try to dig it up from the archives.
--bb
More information about the Digitalmars-d
mailing list