Thread overview
static foreach
Mar 28, 2010
Navin Kumar
Mar 28, 2010
bearophile
Mar 28, 2010
Navin Kumar
Mar 28, 2010
bearophile
Mar 28, 2010
so
Some astonishment [Re: static foreach]
Mar 28, 2010
bearophile
Mar 28, 2010
bearophile
March 28, 2010
I was going over the D tutorials on tuples and noticed that it's possible to do a "foreach" across the tuple items.  I'm assuming this is a compile-time foreach, is that correct?

I've been looking for a way to do a compile-time "foreach" for a different problem, namely complete loop unrolling.  I have a performance-sensitive "atoi" routine that knows, at compile-time, the length of the string (but not the string itself).  I've been thinking of solving this problem using static if(len == 1) { .. } static if(len == 2) { .. } and so on up to len = 22, and not supporting any longer than that.  It severely violates the "DRY" principle, and the function becomes a thousand lines of code (and a nightmare to ever change, since changes have to be made 22x times).

I would like to solve this problem with a compile-time foreach; how do I do so?  "static foreach" is not valid syntax in D.

March 28, 2010
Navin Kumar:

> I was going over the D tutorials on tuples and noticed that it's possible to do a "foreach" across the tuple items.  I'm assuming this is a compile-time foreach, is that correct?

Correct.


> I would like to solve this problem with a compile-time foreach; how do I do so?  "static foreach" is not valid syntax in D.

In my dlibs1 I have:

template Range(int stop) {
    static if (stop <= 0)
        alias Tuple!() Range;
    else
        alias Tuple!(Range!(stop-1), stop-1) Range;
}

/// ditto
template Range(int start, int stop) {
    static if (stop <= start)
        alias Tuple!() Range;
    else
        alias Tuple!(Range!(start, stop-1), stop-1) Range;
}

/// ditto
template Range(int start, int stop, int step) {
    static assert(step != 0, "Range: step must be != 0");

    static if (step > 0) {
        static if (stop <= start)
            alias Tuple!() Range;
        else
            alias Tuple!(Range!(start, stop-step, step), stop-step) Range;
    } else {
        static if (stop >= start)
            alias Tuple!() Range;
        else
            alias Tuple!(Range!(start, stop-step, step), stop-step) Range;
    }
} // End Range!(a,b,c)


With that you can unroll loops and push multiple cases inside a switch statement.
With a little more work you can unroll in cases where you don't know the number of loops, using a modulus and vision approach.

The main problem, baside a bit of bloat at compile time, is that such forach(i; Range!(...)) doesn't work outside functions, as a good static foreach has to.

Bye,
bearophile
March 28, 2010
Thanks.  It would still be nice to see full "static foreach" functionality so that, as you pointed out, it can be used outside of functions, to add member variables to a struct, for instance.   I know people have been using m4 with C to achieve compile-time loops and branches.  D's "static if" is vastly superior to m4 because it has access to typeinfo and routines written in D.

I'm a little confused, though, why the author chose to overload "foreach" to either be a runtime loop or a compile-time loop based on whether its looping over a tuple or not.  It seems it would be more readable to have "static foreach" and simply put a caveat that it's only supported inside functions for now.

March 28, 2010
Navin Kumar:
>It seems it would be more readable to have "static foreach" and simply put a caveat that it's only supported inside functions for now.<

I agree. I have recently written about it: http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=107873

If you see at this document (about half of those ideas are now present in D2) http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf

At page 52 you can see that Walter&Andrei too were appreciating the idea of a static foreach. So I presume you will see it in D3 or even sooner :-) I presume it's not a hard thing to implement. But Walter is busy, and Andrei's book was already late.

Bye,
bearophile
March 28, 2010
> If you see at this document (about half of those ideas are now present in D2)
> http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf

Reading it again, how i missed page 34, awesome!

-- 
Using Opera's revolutionary e-mail client: http://www.opera.com/mail/
March 28, 2010
so:
> Reading it again, how i missed page 34, awesome!

Page 6 is about The Rule of Least Astonishment, D tries hard (and often succeeds) to be uniform, to do the same things when you use the same syntax in different parts of the language.

But I have hitted a case, "T : T*"  can mean different things when used in a template or in a is():


template IsPointer1(T : T*) {
    enum bool IsPointer1 = true;
}
template IsPointer1(T) {
    enum bool IsPointer1 = false;
}
template IsPointer2(T) {
    enum bool IsPointer2 = is(T : T*);
}
void main() {
    static assert(IsPointer1!(int*));
    static assert(IsPointer2!(int*)); // asserts
}


This is a dis-uniformity. I have found it only years of using D1, and it has caused me few troubles.

Bye,
bearophile
March 28, 2010
On 03/28/2010 05:09 PM, bearophile wrote:
> Navin Kumar:
>> It seems it would be more readable to have "static foreach" and simply put a caveat that it's only supported inside functions for now.<
>
> I agree. I have recently written about it:
> http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmars.D&article_id=107873
>
> If you see at this document (about half of those ideas are now present in D2)
> http://s3.amazonaws.com/dconf2007/WalterAndrei.pdf
>
> At page 52 you can see that Walter&Andrei too were appreciating the idea of a static foreach. So I presume you will see it in D3 or even sooner :-) I presume it's not a hard thing to implement. But Walter is busy, and Andrei's book was already late.
>
> Bye,
> bearophile

static foreach was part of the plan until recently, but Walter encountered severe implementation difficulties so we had to abandon it.

I agree that it's odd that the same statement iterates at compile-time or run-time depending on the iterated type.


Andrei
March 28, 2010
Andrei Alexandrescu:
> static foreach was part of the plan until recently, but Walter encountered severe implementation difficulties so we had to abandon it.

I didn't know this.
It looks like a simple thing to implement, but it must be an illusion.


> I agree that it's odd that the same statement iterates at compile-time or run-time depending on the iterated type.

Between implementing a good static foreach that can be used outside functions too, and the current situation, there is an intermediate possibility: to require the prefix keyword "static" before "foreach" when the foreach is done on a tuple, that is when it's static. This changes nothing in the current D2 semantics. But it makes code a little more readable because you can tell apart with no problems the static case of loop on a tuple from the other one.

[Later if Walter finds a way to implement, the static foreach can be made richer, for example letting the compiler translate "static foreach (i; 10 .. 20)" in a "static foreach (i; Range!(10, 20))". And even later if Walter is able the whole thing can be allowed outside functions.]

Bye,
bearophile