www.digitalmars.com         C & C++   DMDScript  

digitalmars.dip.ideas - Foreach application function



The general idea is that the foreach application function answers 
the question: “How do we iterate?” It does so by applying the 
function to the `foreach` aggregate.



Allow a _foreach application function_ lexically between 
`foreach` and the opening parenthesis. It is applied to the 
`foreach` aggregate, otherwise it’s a normal `foreach`. If 
`reverse` were added to object.d, `foreach_reverse` could become 
`foreach reverse` and we could get rid of an oddly specific 
keyword. This pattern reads a lot nicer in many cases than 
applying a function to the `foreach` aggregate.

Practically, `foreach F (…; expr)` just lowers to `foreach (…; 
F(expr))`.

In `foreach F (i; L .. U)`, the foreach application function `F` 
is called with two arguments as `foreach (i; F(L, U))`.

A `foreach` with application function can have more than one 
aggregate:
```d
foreach F (x, y; xs, ys) { … }
// lowers to
foreach F (x, y; F(xs, ys)) { … }
```

The aggregates always must be valid for a `foreach` at least 
insofar that they are:
* static arrays, slices, or associative arrays, or
* have the members for the range interface, or
* have a member `opApply`.

For the case of `..`, the language must ensure `L` and `U` would 
work without the application function.

This is part of the stated objective of foreach application 
functions: They define _how_ to iterate. Their purpose is not to 
enable iteration of what isn’t iterable to begin with.

Otherwise, in principle, aggregates could function as mere 
function arguments to the application function. This is 
undesirable. However, regular function parameters can be passed 
to the aggregate function directly:
```d
foreach F(e) (x, y; xs, ys) { … }
// lowers to
foreach (x, y; F(xs, ys, e)) { … }
```

This is in line with how UFCS calls work.

More than one application function can be allowed:
```d
foreach F(a) G(b, c) (x; xs) { … }
// lowers to
foreach G(b, c) (x; F(xs, a)) { … }
// lowers to
foreach (x; G(F(xs, a), b, c)) { … }
```

(The intermediate step is relevant: The result of `F(xs, a)` must 
be iterable.)

This is also in line with how UFCS calls work: `xs.F(a).G(b, c)` 
is `G(F(xs, a), b, c)`.



Other than `reverse`, there are applications for parallel or 
lockstep or cross-product iteration, probably more.

```d
foreach reverse (i; 0 .. n) {}

enum sameLength = StoppingPolicy.requireSameLength
foreach lockstep(sameLength) reverse (i, x, y; xs, ys) {}

foreach parallel (x; xs) {}
foreach reverse parallel (i; 0 .. n) {}

// somewhat contrived:
alias multiply = t => t[0] * t[1];
foreach zip map!multiply filter!(x => x != 0) (x; xs, ys) {}
```

`reverse` could use DbI to determine if it’s given numeric or 
range/opApply arguments. It implements reverse iteration 
essentially as `iota` does, it forwards a bidirectional range 
interface to the forward range iterface, and makes its `opApply` 
be the argument’s `opApplyReverse`.



While technically, a foreach application function can’t 
distinguish the `..` and `,` case with 2 arguments, the `..` case 
has numeric arguments and the other has iterable arguments. As 
stated above, two comma-separated aggregates will be guaranteed 
by the language to be reasonably close to being iterable, whereas 
slicing-separated arguments are guaranteed to be reasonably close 
to being numeric.



Grammatically, a foreach application functions can’t be an 
arbitrary expression, but must be a 
[*PrimaryExpression*](https://dlang.org/spec/grammar.html#PrimaryExpression)
other than a parenthesized
[*Expression*](https://dlang.org/spec/grammar.html#Expression) with an optional
[*NamedArgumentList*](https://dlang.org/spec/grammar.html#NamedArgumentList) in
parentheses.
Jul 16