View mode: basic / threaded / horizontal-split · Log in · Help
April 27, 2008
Automatic Foreach
I know Walter has his head stuck in Const/Invarient/Pure multithreading 
land at the moment.  I thought I'd fire off this interesting proposal 
anyway.

This is a follow up proposal/suggestion to one I sent a while back.  I 
hope to flesh it out a bit more.

The idea is to automatically generate for loops for arrays that are used 
as parameters.  It will reduces type safety in one area but makes the 
language more expressive, particularly for templates.

Basically:

void foo(char c)
{
 ...
}

...

char[] array;

...

This part:

foreach(auto value; array)
{
  foo(array);
}

becomes:

foo(array);


More advanced stuff:

//////////////////////////////////////////////////
//Example 1
//////////////////////////////////////////////////
// The return value is called multiple times:

float[] array;
array ~= sqrt(array2);

Where sqrt is:

float sqrt(float value);

Would be equivalent to:

int i = array.length;
array.length += array2.length;
foreach(auto val; array2)
{
  array[i++] = sqrt(val);
}

//////////////////////////////////////////////////
//Example 2
//////////////////////////////////////////////////
// When assigned to an array, that array should automatically be resized 
to the number of iterations the function is be called.

float[] array = sqrt(array2);  //array is resized to the correct size

Would be equivalent to:

float[] array;
array.length = array2.length;
int i = 0;
foreach(auto val; array2)
{
  array[i++] = sqrt(val);
}

//////////////////////////////////////////////////
//Example 3
//////////////////////////////////////////////////
// Multiple array inputs should work like nested arrays.

vector[] vertex = dot(array1, array2);

Equivalent too:

vector[] vertex;
vector.length = array1.length * array2.length;
int i=0;
foreach (auto val1; array1)
{
  foreach (auto val2; array2)
  {
    vertex[i++] = sqrt(val1, val2);
  }
}

//////////////////////////////////////////////////
//Example Member functions
//////////////////////////////////////////////////
// This is potentially less useful and could be made illegal.  I'll 
mention it for completeness.

class A
{
  void foo();
}

...

A[] a;

...

a.foo();  //Call foo for the length of a


Equivalent to:

foreach (auto val; a)
{
   a.foo();
}

//////////////////////////////////////////////////
//Example Return types
//////////////////////////////////////////////////

proccess(array).foo().foo2();

Equivalent too:

foreach (auto val; )
{
  proccess(val).foo().foo2();
}

//////////////////////////////////////////////////
//Use example nested
//////////////////////////////////////////////////


results ~= func(foo(array1), foo2(array2));

Equivalent too:

results.length += array1.length * array2.length;
int i = results.length;
foreach (auto val1; array1)
{
  foreach (auto val2; array2)
  {
     results[i++] = func(foo(val1), foo2(val2));
  }
}


//////////////////////////////////////////////////
//Use example
//////////////////////////////////////////////////

results ~= func(foo(array1)).method(array2);

results.length += array1.length * array2.length;
int i = results.length;
foreach (auto val1; array1)
{
  foreach (auto val2; array2)
  {
     results[i++] = func(foo(val1)).method(val2);
  }
}

//////////////////////////////////////////////////
//Use example with templates
//////////////////////////////////////////////////

void print(A ...)(A a)
{
  write(a);
}

print(array1, array2, value);

//What happens here is the array is passed into the template, because an 
array is a valid input into a template.  The value is only evaluated 
when inside the template. See overloading rules below.

So it's equivalent to:

foreach (auto val; array1)
{
  write(val);
}

foreach (auto val; array2)
{
  write(val);
}

Overloading rules:

This auto foreach is given the lowest priority.  If there's already a 
way to call the function then that will be used.  That will enable 
people to specialize how arrays are handled for different functions be 
overloading it.  Templates for example, an array is a valid input so it 
won't call the template function multiple times.

Working with the future:
The proposal should work well with features such as pure functions.  It 
should be as optimal as foreach statements however the compiler would 
have to do less work to realize optimizations.

Interchangeable methods/functions: If this feature comes in, I don't see 
any problem with the proposal working.

Rantional:
Half the time I create a for loop I endup encapsulating it in a 
function.  If D automatically created the function it would be one less 
thing to worry about.  If I need specialize behavior I could always 
specialize the function later by providing an overload.

I think having this functionality would remove much of the need for 
being able to iterate over 2 arrays at the same time.

Comment:
I know python has syntax not unlike this, however I believe the above is 
even simpler.

What do you think?
April 27, 2008
Re: Automatic Foreach
Something at the back of my mind tells me that, once upon a time,
Walter may have planned something like that for D, whereby

   a[] = n;
   a[] = b[];
   a[] = b[] + c[];
   a[] = f(b[]);

would mean

   foreach(ref e;a) e = n;
   foreach(i,ref e;a) e = b[i];
   foreach(i,ref e;a) e = b[i] + c[i];
   foreach(i,ref e;a) e = f(b[i]);

respectively. But if so, the plan got dropped a long time ago, and now
only the first two cases work.

(Disclaimer: I may have got that completely wrong).

I don't see the problem with the foreach version though. It's
certainly transparent.
April 27, 2008
Re: Automatic Foreach
On Sun, 27 Apr 2008 10:45:36 +0400, janderson <askme@me.com> wrote:
>
> //////////////////////////////////////////////////
> //Example 3
> //////////////////////////////////////////////////
> // Multiple array inputs should work like nested arrays.
>
> vector[] vertex = dot(array1, array2);
>
> Equivalent too:
>
> vector[] vertex;
> vector.length = array1.length * array2.length;
> int i=0;
> foreach (auto val1; array1)
> {
>    foreach (auto val2; array2)
>    {
>      vertex[i++] = sqrt(val1, val2);
>    }
> }
>

Did you mean dot(val1, val2); here?
IMO, it saves you a little of typing, but you loose control over code  
execution.
It is hard to debug, it adds additional ambiguity.

f(char[] s);
f(char c);

char[] chars;
f(chars); // f(char[] s) is called, but I need foreach(), what should I do?

It is easy to write a template, that would do what you want:

template ReturnType(CallableType, TList...)
{
    CallableType c;
    TList u;
    static if (is(typeof(c(u)) == void)) {
        alias void Value;
    } else {
        alias typeof(c(u))[] Value;
    }
}

template auto_foreach(CallableType, ElementType)
{
    ReturnType!(CallableType,ElementType).Value auto_foreach(CallableType  
callable, ElementType[] elements)
    {
        alias ReturnType!(CallableType,ElementType).Value Type;
        static if (is(Type == void)) {
            foreach(e; elements) {
                callable(e);
            }
        } else {
            Type result;
            result.length = elements.length;
            int i = 0;
            foreach (e; elements) {
                result[i++] = callable(e);
            }
            return result;
        }
    }
}

void putc(char c)
{
    printf("%c", c);
}

char shift(char c)
{
    return c+1;
}

int main(string[] args)
{
    char[] h = "hello";
    h = auto_foreach(&shift, h);
    auto_foreach(&putc, h);

    return 0;
}

You could also change this template to accept multiple arrays as input.  
It's easy (use variadic template + recursion).
Make more practice in generic programming, it is worth it!
April 27, 2008
Re: Automatic Foreach
Janice Caron wrote:
> Something at the back of my mind tells me 

Maybe its the compiler error message that says "vector operations are 
not implemented" that's telling you that.  :-)  (as opposed to just 
plain "syntax error")

> that, once upon a time,
> Walter may have planned something like that for D, whereby
> 
>     a[] = n;
>     a[] = b[];
>     a[] = b[] + c[];
>     a[] = f(b[]);
> 
> would mean
> 
>     foreach(ref e;a) e = n;
>     foreach(i,ref e;a) e = b[i];
>     foreach(i,ref e;a) e = b[i] + c[i];
>     foreach(i,ref e;a) e = f(b[i]);
> 
> respectively. But if so, the plan got dropped a long time ago, and now
> only the first two cases work.
> 
> (Disclaimer: I may have got that completely wrong).
> 
> I don't see the problem with the foreach version though. It's
> certainly transparent.
April 27, 2008
Re: Automatic Foreach
janderson wrote:
> I know Walter has his head stuck in Const/Invarient/Pure multithreading 
> land at the moment.  I thought I'd fire off this interesting proposal 
> anyway.
> 
> This is a follow up proposal/suggestion to one I sent a while back.  I 
> hope to flesh it out a bit more.
> 
> The idea is to automatically generate for loops for arrays that are used 
> as parameters.  It will reduces type safety in one area but makes the 
> language more expressive, particularly for templates.
> 
> Basically:
> 
> void foo(char c)
> {
>  ...
> }
> 
> ...
> 
> char[] array;
> 
> ...
> 
> This part:
> 
> foreach(auto value; array)
> {
>   foo(array);
> }
> 
> becomes:
> 
> foo(array);
> 
> 
> More advanced stuff:
> 
> //////////////////////////////////////////////////
> //Example 1
> //////////////////////////////////////////////////
> // The return value is called multiple times:
> 
> float[] array;
> array ~= sqrt(array2);
> 
> Where sqrt is:
> 
> float sqrt(float value);
> 
> Would be equivalent to:
> 
> int i = array.length;
> array.length += array2.length;
> foreach(auto val; array2)
> {
>   array[i++] = sqrt(val);
> }
> 
> //////////////////////////////////////////////////
> //Example 2
> //////////////////////////////////////////////////
> // When assigned to an array, that array should automatically be resized 
> to the number of iterations the function is be called.
> 
> float[] array = sqrt(array2);  //array is resized to the correct size
> 
> Would be equivalent to:
> 
> float[] array;
> array.length = array2.length;
> int i = 0;
> foreach(auto val; array2)
> {
>   array[i++] = sqrt(val);
> }
> 
> //////////////////////////////////////////////////
> //Example 3
> //////////////////////////////////////////////////
> // Multiple array inputs should work like nested arrays.
> 
> vector[] vertex = dot(array1, array2);
> 
> Equivalent too:
> 
> vector[] vertex;
> vector.length = array1.length * array2.length;
> int i=0;
> foreach (auto val1; array1)
> {
>   foreach (auto val2; array2)
>   {
>     vertex[i++] = sqrt(val1, val2);
>   }
> }
> 
> //////////////////////////////////////////////////
> //Example Member functions
> //////////////////////////////////////////////////
> // This is potentially less useful and could be made illegal.  I'll 
> mention it for completeness.
> 
> class A
> {
>   void foo();
> }
> 
> ...
> 
> A[] a;
> 
> ...
> 
> a.foo();  //Call foo for the length of a
> 
> 
> Equivalent to:
> 
> foreach (auto val; a)
> {
>    a.foo();
> }
> 
> //////////////////////////////////////////////////
> //Example Return types
> //////////////////////////////////////////////////
> 
> proccess(array).foo().foo2();
> 
> Equivalent too:
> 
> foreach (auto val; )
> {
>   proccess(val).foo().foo2();
> }
> 
> //////////////////////////////////////////////////
> //Use example nested
> //////////////////////////////////////////////////
> 
> 
> results ~= func(foo(array1), foo2(array2));
> 
> Equivalent too:
> 
> results.length += array1.length * array2.length;
> int i = results.length;
> foreach (auto val1; array1)
> {
>   foreach (auto val2; array2)
>   {
>      results[i++] = func(foo(val1), foo2(val2));
>   }
> }
> 
> 
> //////////////////////////////////////////////////
> //Use example
> //////////////////////////////////////////////////
> 
> results ~= func(foo(array1)).method(array2);
> 
> results.length += array1.length * array2.length;
> int i = results.length;
> foreach (auto val1; array1)
> {
>   foreach (auto val2; array2)
>   {
>      results[i++] = func(foo(val1)).method(val2);
>   }
> }
> 
> //////////////////////////////////////////////////
> //Use example with templates
> //////////////////////////////////////////////////
> 
> void print(A ...)(A a)
> {
>   write(a);
> }
> 
> print(array1, array2, value);
> 
> //What happens here is the array is passed into the template, because an 
> array is a valid input into a template.  The value is only evaluated 
> when inside the template. See overloading rules below.
> 
> So it's equivalent to:
> 
> foreach (auto val; array1)
> {
>   write(val);
> }
> 
> foreach (auto val; array2)
> {
>   write(val);
> }
> 
> Overloading rules:
> 
> This auto foreach is given the lowest priority.  If there's already a 
> way to call the function then that will be used.  That will enable 
> people to specialize how arrays are handled for different functions be 
> overloading it.  Templates for example, an array is a valid input so it 
> won't call the template function multiple times.
> 
> Working with the future:
> The proposal should work well with features such as pure functions.  It 
> should be as optimal as foreach statements however the compiler would 
> have to do less work to realize optimizations.
> 
> Interchangeable methods/functions: If this feature comes in, I don't see 
> any problem with the proposal working.
> 
> Rantional:
> Half the time I create a for loop I endup encapsulating it in a 
> function.  If D automatically created the function it would be one less 
> thing to worry about.  If I need specialize behavior I could always 
> specialize the function later by providing an overload.
> 
> I think having this functionality would remove much of the need for 
> being able to iterate over 2 arrays at the same time.
> 
> Comment:
> I know python has syntax not unlike this, however I believe the above is 
> even simpler.
> 
> What do you think?

I think it's already hard enough to figure out what's going to get 
called by something like foo(array)

It could be
- foo(int[] x)
- foo(int[] x...)
- foo(T)(T x)
- foo(T)(T[] x)
- foo(T...)(T x)
- struct foo { static foo opCall(int[]); }
- struct foo { static foo opCall(int[]...); }
- struct foo { static foo opCall(T)(T x); }  etc
- or struct Foo with non-static opCall on instance foo
- or all the same stuff on class  ...

So I don't think another meaning for foo(array) is really helpful.

I *do* like the idea of an expression (not a statement) that has 
foreach-like abilities.  But I think it should come with some 
distinguishing syntax.

In Python it's just [expr(x) for x in array]
Which resonates with Pythons normal loopoing:
   for x in array: expr(x)

So direct translation of that idea to D would be
   [expr(x) foreach(x; array)];

Seems not so terrible a syntax to me.

--bb
April 27, 2008
Re: Automatic Foreach
Janice Caron wrote:
> Something at the back of my mind tells me that, once upon a time,
> Walter may have planned something like that for D, whereby
> 
>     a[] = n;
>     a[] = b[];
>     a[] = b[] + c[];
>     a[] = f(b[]);
> 
> would mean
> 
>     foreach(ref e;a) e = n;
>     foreach(i,ref e;a) e = b[i];
>     foreach(i,ref e;a) e = b[i] + c[i];
>     foreach(i,ref e;a) e = f(b[i]);
> 
> respectively. But if so, the plan got dropped a long time ago, and now
> only the first two cases work.
> 
> (Disclaimer: I may have got that completely wrong).
> 
> I don't see the problem with the foreach version though. It's
> certainly transparent.

This raises an interesting point.  So the typesaftly issue could be 
solved like:

foo(array[]);

-Joel
April 27, 2008
Re: Automatic Foreach
Bill Baxter wrote:
> janderson wrote:
>> What do you think?
> 
> I think it's already hard enough to figure out what's going to get 
> called by something like foo(array)
> 
> It could be
> - foo(int[] x)
> - foo(int[] x...)
> - foo(T)(T x)
> - foo(T)(T[] x)
> - foo(T...)(T x)
> - struct foo { static foo opCall(int[]); }
> - struct foo { static foo opCall(int[]...); }
> - struct foo { static foo opCall(T)(T x); }  etc
> - or struct Foo with non-static opCall on instance foo
> - or all the same stuff on class  ...
> 
> So I don't think another meaning for foo(array) is really helpful.
> 
> I *do* like the idea of an expression (not a statement) that has 
> foreach-like abilities.  But I think it should come with some 
> distinguishing syntax.
> 
> In Python it's just [expr(x) for x in array]
> Which resonates with Pythons normal loopoing:
>    for x in array: expr(x)
> 
> So direct translation of that idea to D would be
>    [expr(x) foreach(x; array)];
> 
> Seems not so terrible a syntax to me.
> 
> --bb

I think I prefer:

foo(array[]);

-Joel
April 27, 2008
Re: Automatic Foreach
janderson wrote:
> Bill Baxter wrote:
>> janderson wrote:
>>> What do you think?
>>
>> I think it's already hard enough to figure out what's going to get 
>> called by something like foo(array)
>>
>> It could be
>> - foo(int[] x)
>> - foo(int[] x...)
>> - foo(T)(T x)
>> - foo(T)(T[] x)
>> - foo(T...)(T x)
>> - struct foo { static foo opCall(int[]); }
>> - struct foo { static foo opCall(int[]...); }
>> - struct foo { static foo opCall(T)(T x); }  etc
>> - or struct Foo with non-static opCall on instance foo
>> - or all the same stuff on class  ...
>>
>> So I don't think another meaning for foo(array) is really helpful.
>>
>> I *do* like the idea of an expression (not a statement) that has 
>> foreach-like abilities.  But I think it should come with some 
>> distinguishing syntax.
>>
>> In Python it's just [expr(x) for x in array]
>> Which resonates with Pythons normal loopoing:
>>    for x in array: expr(x)
>>
>> So direct translation of that idea to D would be
>>    [expr(x) foreach(x; array)];
>>
>> Seems not so terrible a syntax to me.
>>
>> --bb
> 
> I think I prefer:
> 
> foo(array[]);
> 
> -Joel


That could work with member functions too:

array[].foo();

-Joel
April 27, 2008
Re: Automatic Foreach
janderson wrote:
> janderson wrote:
>> Bill Baxter wrote:
>>> janderson wrote:
>>>> What do you think?
>>>
>>> I think it's already hard enough to figure out what's going to get 
>>> called by something like foo(array)
>>>
>>> It could be
>>> - foo(int[] x)
>>> - foo(int[] x...)
>>> - foo(T)(T x)
>>> - foo(T)(T[] x)
>>> - foo(T...)(T x)
>>> - struct foo { static foo opCall(int[]); }
>>> - struct foo { static foo opCall(int[]...); }
>>> - struct foo { static foo opCall(T)(T x); }  etc
>>> - or struct Foo with non-static opCall on instance foo
>>> - or all the same stuff on class  ...
>>>
>>> So I don't think another meaning for foo(array) is really helpful.
>>>
>>> I *do* like the idea of an expression (not a statement) that has 
>>> foreach-like abilities.  But I think it should come with some 
>>> distinguishing syntax.
>>>
>>> In Python it's just [expr(x) for x in array]
>>> Which resonates with Pythons normal loopoing:
>>>    for x in array: expr(x)
>>>
>>> So direct translation of that idea to D would be
>>>    [expr(x) foreach(x; array)];
>>>
>>> Seems not so terrible a syntax to me.
>>>
>>> --bb
>>
>> I think I prefer:
>>
>> foo(array[]);
>>
>> -Joel
> 
> 
> That could work with member functions too:
> 
> array[].foo();
> 
> -Joel

And slices:

array[0..5].foo();

-Joel
April 27, 2008
Re: Automatic Foreach
janderson wrote:
> Bill Baxter wrote:
>> janderson wrote:
>>> What do you think?
>>
>> I think it's already hard enough to figure out what's going to get 
>> called by something like foo(array)
>>
>> It could be
>> - foo(int[] x)
>> - foo(int[] x...)
>> - foo(T)(T x)
>> - foo(T)(T[] x)
>> - foo(T...)(T x)
>> - struct foo { static foo opCall(int[]); }
>> - struct foo { static foo opCall(int[]...); }
>> - struct foo { static foo opCall(T)(T x); }  etc
>> - or struct Foo with non-static opCall on instance foo
>> - or all the same stuff on class  ...
>>
>> So I don't think another meaning for foo(array) is really helpful.
>>
>> I *do* like the idea of an expression (not a statement) that has 
>> foreach-like abilities.  But I think it should come with some 
>> distinguishing syntax.
>>
>> In Python it's just [expr(x) for x in array]
>> Which resonates with Pythons normal loopoing:
>>    for x in array: expr(x)
>>
>> So direct translation of that idea to D would be
>>    [expr(x) foreach(x; array)];
>>
>> Seems not so terrible a syntax to me.
>>
>> --bb
> 
> I think I prefer:
> 
> foo(array[]);

I think that already means call foo with a full slice of array, doesn't 
it?  Anyway, even if it doesn't you can overload opSlice() currently so 
that array[] can mean anything you want it to.

--bb
« First   ‹ Prev
1 2 3 4
Top | Discussion index | About this forum | D home