www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 2498] New: make foreach work for any callable symbol

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.


-- 
Dec 08 2008