Proposal: Hide the int in opApply from the user

Bill Baxter dnewsgroup at billbaxter.com
Mon Jan 7 01:06:52 PST 2008


I proposed this iniitally over in D.learn, but I'm cleaning it up and 
reposting here in hopes of getting some response from Walter who was 
probably too busy finishing const and eating holiday Turkey at the time 
to notice.  And rightly so.

I don't believe it's appropriate in a high-level supposedly clean 
language like D that one of the main facilities for iterating over user 
types (foreach) requires writing code that passes around magic values 
generated by the compiler (opApply).

It seems wrong to me that these magic values
- come from code generated by the compiler,
- must be handled exactly the proper way by the user's opApply
    (or else you get undefined behavior, but no compiler errors)
- and then are handed back to code also generated by the compiler.

Furthermore, the compiler-generated code in the first and last steps 
share the same scope!  So the solution seems obvious -- they should pass 
the information back and forth using a local variable in their shared 
local scope.

In this proposal we need to add two things:

1) a new template struct and
2) a new macro [yes, this proposal relies on macros which don't exist yet!]


The template just bundles an int* (pointer to _ret) together with the 
loop body delegate:

   struct Apply(Args...)
   {
     alias void delegate(Args) LoopBody;
     LoopBody _loop_body;
     int* _ret = null;
   }

the macro is this (just guessing what syntax will be, and hoping macros 
will support tuple-like varargs):

   macro yield(dg, args...) {
     dg._call(args);
     if (dg._ret && *dg._ret) { return; }
   }

With these two library additions, opApply functions can become this:

void opApply( Apply!(ref T) dg ) {
    for( /*T x in elements*/ ) {
        yield(dg,x);
    }
}

Now the trickiness is *all* shifted to how you call such a beast 
properly, which is all handled by the compiler.  For a foreach in a void 
function, the compiler will have to generate code like so:

     int _ret = 0;
     void _loop_body(/*ref*/ T x)
     {
         writefln("x is ", x);
         if (x=="two") { _ret = BREAK; return; }
         if (x=="three") { _ret = RETURN; return; }
         do_something;
     }
     obj.opApply( Apply!(T)(&_loop_body, &_ret) );
     if (_ret==RETURN) return;


The language can ALMOST do this today except for three small things:
1) No macros - but they're on the way!
2) Inability to preserve ref-ness of template arguments -- but I think 
this really needs to be solved one way or another regardless.
3) The necessary but changes to the foreach code gen -- this is 
straightforward.


Attached is a proof of concept demo.  I've manually inlined the yield() 
code to work around 1), and made the loop body use a non-ref type to 
work around 2).  I manually generated the foreach code too to deal with 3).

The great thing about this proposal is that it is backwards compatible. 
   foreach already generates different code depending on what the 
argument is, this can just be another case detected by the use of the 
Apply argument.  Code using old-style opApplys can continue to work.

The main thing fuzzy in my mind is the vague status of yield and Apply. 
  They don't need to be keywords per-se, but the compiler at least needs 
to know about Apply so that it can recognize the signature of this 
"new-style" opApply.   I think it can maybe satisfy all that by going 
into object.d?  If there were anonymous struct literals it wouldn't even 
need to be a real struct, just an alias like we have for 'string' now.


--bb
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: newforeach.d
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20080107/31d696d5/attachment.ksh>


More information about the Digitalmars-d mailing list