October 17, 2006
Walter Bright wrote:
> Walter Bright wrote:
>> Added foreach_reverse, which addresses a serious shortcoming.
>>
>> http://www.digitalmars.com/d/changelog.html
> 
> 
> Lots of background for the foreach improvements in:
> 
> http://www.digitalmars.com/d/archives/digitalmars/D/17320.html

*Jaw drops on floor*

-- 
-----BEGIN GEEK CODE BLOCK-----
Version: 3.1
GCS/MU/S d-pu s:+ a-->? C++++$ UL+++ P--- L+++ !E W-- N++ o? K? w--- O M--@ V? PS PE Y+ PGP- t+ 5 X+ !R tv-->!tv b- DI++(+) D++ G e++>e h>--->++ r+++ y+++
------END GEEK CODE BLOCK------

James Dunne
October 17, 2006
Frits van Bommel wrote:
> Walter Bright wrote:
>> Hasan Aljudy wrote:
>>> Also, since foreach already takes two arguments (int i, T t) that is, index and element, you can add a third argument, of bool type, with the meaning "start in reverse mode"
>>> foreach( true, i, d; "Hello" )
>>> {
>>>     writefln( d );
>>> }
>>>
>>> should print:
>>> o
>>> l
>>> l
>>> e
>>> H
>>
>> That would work, and something like it has been suggested before, but it is too obscure looking. When someone sees "foreach_reverse", I think it'll be very clear what's going on.
> 
> There's no need to use booleans (at least, not directly):
> 
> enum Ordering : bool { forward, backwards, reversed = backwards }
> 
> That should produce much more readable code, but work the same. Can be done right now, except you can't overload opApply for arrays :(. (Tell me if I'm wrong, but I tried and it didn't work)

I like that the current implementation will produce a compile error if an object without opApplyReverse is used as an aggregate in foreach_reverse.  If the syntax were to change, I wouldn't want to push this checking to run-time (which it seems a parameter-based method might do).


Sean
October 17, 2006
Well, I feel a little naggy again so take this with as much salt as you think it warrants.  But I'm beyond really getting worked up about the D sega anymore; it mostly won't matter or doesn't matter to me... much.  Committee or not, Walter seems to make language design an ... um... interesting pasttime: his decisions are sometimes excellent, sometimes spontaneous and abrupt, sometimes unwarranted.  Why leave to a committee what one designer can accomplish with flourish. ;D

I have to agree with Sean and Ary.  My own opinion: I don't really understand why "foreach_reverse" was, once again, just tossed into the language with (what seems to be) a minimum of discussion?  Or should I really be surprised anymore -- this is becoming a regular habit of his now, and I begin to wonder what's motivating him... maybe a 1.0 version?  Yet, I do recall that Walter was getting itchy fingers when he realized that a reverse iterator was necessary to make D competitive with C++ (?).  And here we have it, another quick addition of an ugly syntax, a tacky looking keyword that is as lovely as a bandaid bridging a broken bone.

I thought D was about clean design. Isn't the trick for fixing this less about adding another keyword and more about thinking through a solution that's widely applicable.  Does "foreach_reverse" really qualify as orthogonal?  If we look at other languages (especially functional varieties) adding a keyword for each new traversal syntax doesn't appear to be the order of the day.  If I recall correctly it's more about properties and modifiers of the list/expressions themselves (not sure if that was worded clearly). Cannot foreach be extended in the same manner... "foreach" appears to fit its purpose well. As Sean indicated: "that foreach doesn't imply an order in which elements will be visited".  I'm sure the keyword can be reused in the fashion of reversal as well.

Well, will you look at that.  Isn't that irony?  We got our rolls reversed, I think.  I used to be Walter that was hesitant to add keywords to the language.  Go figure! :D

-JJR
October 17, 2006
John Reimer wrote:
> I have to agree with Sean and Ary.  My own opinion: I don't really
> understand why "foreach_reverse" was, once again, just tossed into
> the language with (what seems to be) a minimum of discussion?

There was quite a bit of discussion. The thread was a bit old, but that doesn't make it less relevant:

http://www.digitalmars.com/d/archives/digitalmars/D/17320.html

> Well, will you look at that.  Isn't that irony?  We got our rolls
> reversed, I think.  I used to be Walter that was hesitant to add
> keywords to the language.  Go figure! :D

I was reluctant to do it for a long time for that reason. It's just that no better solution emerged.
October 17, 2006
Walter said:
" There was quite a bit of discussion. The thread was a bit old, bt
that doesn't make it less relevant: "

Ah, ok.  I stand corrected on that aspect of my critique.

-JJR
October 17, 2006
Tom S wrote:

<snip>great proposal</snip>

+1, yet another high level feature that reminds me of Ruby. :)
October 17, 2006
John Reimer wrote:
> Ah, ok.  I stand corrected on that aspect of my critique.

I'll give some more lame justifications:

There's been some talk about Boost recently in the n.g. (here and in comp.lang.c++) about if what Boost does is really worth it, or if it's just a lot of show-how-clever-I-am falderol. The general idea among the Boost people is that if there's any way possible it could be done in a library, rather than the core, then that's the way it should be done.

I don't agree with that idea. I think Boost stretches C++ past the breaking point, and rather than showing how powerful C++ is, shows instead that the C++ core lacks power. Some of the shortcoming workarounds in Boost are just incredible, in the dogged persistence of their inventors to somehow make them work. Even so, the Boost results still wind up with serious holes in them.

If some crucial features are added to the core language, then much of Boost either becomes irrelevant, or at least becomes far simpler and straightforward to implement.

So let's take a simple example, not from Boost, but from its earlier incarnation STL. STL has an algorithms section, and one of those is for_each. A glaring shortcoming of for_each is you have to supply a function pointer to it - you cannot just supply a statement in { }. With foreach as a supported core language statement, it can be made to work in the general case, and it can be made to generate relevant error messages when used incorrectly. foreach is so darned useful, it seems a shame to have to do it with clumsy, limited hacks like std::for_each.

STL generalizes iteration across a collection using a special iterator type. One needs to be created for each collection class. The iterators are then used in the implementation of STL algorithms. There are 3 kinds of iterators: forward, reverse, and random access. There's a serious problem with iterators, though - they require the collection to be accessible in a serialized way, when many data structures (such as binary trees) are not easily traversed that way (recursive descent for them).

So I've been interested in having D algorithms and collections not need iterator types at all. I've been experimenting with doing STL's algorithms in D, and it became clear that to make them complete, reverse iteration was necessary. foreach handles forward iteration, and opIndex handles random access. Various ways of doing reverse traversal without core support always seemed to involve creating dummy classes and other hackish stuff. Promoting it to a supported statement makes it pretty clean for the user to understand and use.

Essentially, I think foreach_reverse is the missing piece to be able to implement all of STL's algorithms code for D.
October 17, 2006
Frank Benoit (keinfarbton) wrote:
> see bug report 440

Does that need a fix right away?
October 17, 2006
Tom S wrote:
> void each(int[] a, void delegate(int) dg, inout void* ret) {
It would be good to support non-void delegates as well, for things with predicates, like map, fold, and sort:

 int fold(int[] a, int start, void delegate(int, int) dg, inout void* ret)

 and

 fold(myList, 0) (a, b) { yield a + b; } // Yield returns a value from the delegate, as opposed to returning from the enclosing function


Note that with some predicates shouldn't be able to control the flow of code, so I think that last parameter (inout void* ret) should be optional -- if the last parameter is a delegate, then break and return can't be used within the delegate.
>     for (int i = 0; i < a.length; ++i) {
>         dg(a[i]);
>         if (ret) return;
>     }
> }
> 

>     each (arr) (int a) {
Of course, this should be type-inferred so you just need to write

each (arr) (a) { ... }
  or
arr.each (a) { ... }

Cheers,

Reiner
October 17, 2006
On Tue, 17 Oct 2006 18:17:33 -0400, Walter Bright <newshound@digitalmars.com> wrote:

> Frank Benoit (keinfarbton) wrote:
>> see bug report 440
>
> Does that need a fix right away?

Broke a lot of my projects; went back to DMD 0.169 for now.