December 08, 2008
http://d.puremagic.com/issues/show_bug.cgi?id=2498

           Summary: make foreach work for any callable symbol
           Product: D
           Version: 2.021
          Platform: PC
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: bugzilla@digitalmars.com
        ReportedBy: schveiguy@yahoo.com


A cool feature I just discovered with D is that I can do

foreach(x; y)

where y is a delegate, not a class or struct with opApply.

What is missing from this is the ability to use a function for y without making it a delegate.

For example, the following does not work:

class C
{
   int func(int delegate(ref char c) dg) {...}
   int func(int delegate(ref uint i, ref char c) dg) {...}
}

void foo(C c)
{
   foreach(i, x; c.func) // Error, c.func() is not valid
   ...
}

What is happening is that c.func is being interpreted by the compiler as a call to c.func with 0 arguments (i.e. a property).  So one must use a delegate:

void foo(C c)
{
   foreach(i, x; &c.func) // Error, cannot infer arguments
}

Oops, because taking the address of c.func automatically refers to the first version, I can't easily use the second version.  This works:

void foo(C c)
{
   int delegate(int delegate(ref uint, ref char)) dg = &c.func;
   foreach(i, x; dg)
   ...
}

But oh how horrid that is!

Note that we have no such restriction on opApply.  If func was opApply, I would not need to specify which opApply I'm referring to.

I think it would be safe to consider the aggregate as a function call in addition to a delegate might be the best solution.  Then the normal lookup rules could be used.  This is because:

foreach(i, x; c.func)
{
  // body
}

Should literally translate to:

int foreach_body(auto_discovered_type1 i, auto_discovered_type2 x)
{
  // body
}
int foreach_result = c.func(&foreach_body);
// handle result properly.

Unless, of course, the aggregate is actually an aggregate, in which case, opApply is appended.  This is how I propose the compiler treats a foreach statement.

What you lose is the ability to have a property that returns a delegate be used as an aggregate to foreach.  But this is no loss in my opinion, I don't know of any code that returns a delegate as a property to be used in a foreach clause, except code trying to work around the requirement that foreach takes a delegate.  And this is easily worked around by putting the parens for the property.  It is impossible to work around the current incarnation.

More benefits:

- No intermediate delegate is created (less crap to pass around, or to optimize
out)
- It would be possible to use functors also (with opCall).  You might even be
able to remove opApply in favor of opCall, to make things more uniform (I'd
guess very little conflict with existing opCall functions, as opCall is
typically not used much with an opApply type signature).
- Get rid of foreach_reverse (really easy to have a differently named function
on an aggregate instead of a different keyword).
- If you consider that the compiler just translates the code, you could use
extension functions on arrays also.  i.e. if you define a function int
foo(T)(T[], int delegate(ref int x, ref int y, ref T v)), then you could do
foreach(x, y, v; array_instance.foo).

I'd say removing opApply would be too much of a breaking change for D1, but allowing using function names instead of requiring delegates wouldn't be too much of a change IMO.


--