October 18, 2006
Ok, no prob, I'll get it fixed.
October 18, 2006
Tom S wrote:
> 
> I'm not yet fully convinced if trailing delegates should be allowed implicitly, just because a function's signature matches some criteria. A special keyword like 'trailing' could be used for it. Of course, because of a lack of a better keyword at the moment, we could just call it 'static' <g>

Versus plain old delegates, trailing delegates can allow:
1. type inference of parameters
2. syntactic sugar to remove ';' at the end, making it look more native
3. continue/break/return behaviour.

1 and 2 are syntactic sugar for the call-site; therefore, they should be allowed implicitly.

3 is special behaviour and shouldn't be allowed implicitly. However, I don't think a new keyword is required. Just make an enum:

enum IterationResult { CONTINUE, BREAK, RETURN };

and the break/continue behaviour is then only possible when the function has a delegate as a second-last parameter, and IterationResult as the last.

Cheers,

Reiner
October 18, 2006
Tom S wrote:
> Bill Baxter wrote:
>> In C++ I would just make it a reference parameter, and take the address:
>>
>> void print_addr( IntArrayType& arg )
>> {
>>     writefln(&arg);
>> }
>> void main()
>> {
>>     int[] arr = [1,2,3,4,5];
>>     writefln(&arr);
>>     print_addr(arr);
>> }
>>
>> Then aside from the fact that D is not C++, it would print the same value for both, and it wouldn't be accessing a static variable.  And it would be perfectly thread safe.
> 
> 
> Heh, that's not the same case. You can do that it D, except that you'd use inout for the reference. The problem arises when you try to return a delegate literal from a function, it then references stack data which is invalid after the function returns. Speaking of C++ analogies, it is just like returning pointers to stack data. 

I see.  I was mislead into thinking that closures worked because this code compiled without complaint and worked fine in release mode:

int delegate(int) AddN(int N)
{
    int _addN(int x) {
        return x + N;
    }
    return &_addN;
}
writefln(AddN(3)(4));


But after recompiling with -g it gives me a garbage answer.  I thought I could continue to access the _value_ of the N


BUT! making a local copy of N in the function, it seems to work:

| int delegate(int) AddN(int N)
| {
|     int _addN(int x) {
|         int MyN = N;
|         return x + MyN;
|     }
|     return &_addN;
| }
| writefln(AddN(3)(4));

(prints 7 in debug mode too)


And applying the same idea, this works too:


int delegate(int delegate(inout typeof(AggregateT[0])))
reversed(AggregateT)(inout AggregateT array)
{
    alias typeof(AggregateT[0]) ElemT;
    int _innerFunc(int delegate(inout ElemT) loopBody)
    {
        AggregateT myArr = array;  // make a local ref!
        for (int i = myArr.length-1; i >=0; i--)
        {
            if (loopBody(myArr[i]))
                break;
        }
        return -99;
    }
    return &_innerFunc;
}

int[] aint = [1,2,3,4,5,6,7];
foreach(int i; reversed(aint)) { ... }

So is that not guaranteed to work?
If it is ok then we have a way to do reversed(foo) without dummy classes!  (I'll put aside the issue of "hackish stuff" for the moment.)

Syntactically and functionally it's not much different, but compared with the struct version it doesn't leak implementation issues to the outside world like having a reverse__ struct dangling out there does.

So now, how to make it look less hackish...

--bb
October 18, 2006
Oskar Linde wrote:
> Tom S wrote:
> 
> [snip proposal]
> 

> The possibilities seem endless. It is almost scary. And best of all: It doesn't contain a single template. :)

Er, but what if you want to support more than int[] as the type to be looped over?

> void each(int[] a, void delegate(int) dg, inout void* ret) {
>     for (int i = 0; i < a.length; ++i) {
>         dg(a[i]);
>         if (ret) return;
>     }
> }

Looks to me like you've got you an int[]-specific looper there.  Gonna need some templates to make that generic, no?

--bb
October 18, 2006
Bill Baxter wrote:
> Oskar Linde wrote:
>> Tom S wrote:
>>
>> [snip proposal]
>>
> 
>> The possibilities seem endless. It is almost scary. And best of all: It doesn't contain a single template. :)
> 
> Er, but what if you want to support more than int[] as the type to be looped over?
> 
>  > void each(int[] a, void delegate(int) dg, inout void* ret) {
>  >     for (int i = 0; i < a.length; ++i) {
>  >         dg(a[i]);
>  >         if (ret) return;
>  >     }
>  > }
> 
> Looks to me like you've got you an int[]-specific looper there.  Gonna need some templates to make that generic, no?

Yes. For the built in arrays, you would need either templates, or do as phobos does internally, work with void * and TypeInfo.

The construct in itself is template less. But if you make your own generic container, you will of course need to make your iterator generic too.

The "best of all"-part was a joke. :)

/Oskar
October 18, 2006
Walter Bright wrote:
> Bill Baxter wrote:
>> I don't see how it helps.  If you can already do:
>>    foreach(T item; &collection.reversed()) { }
> 
> That doesn't work for arrays.

In what way does it not work? I have been doing:

foreach(x; "abcd".reverseView())
	writef("%s",x);

prints "dcba"

For any type of built in array for a very long time (long before 0.170), and it certainly seems to work for me.

I also do things like:

foreach(x; "aBcDeF".select(&isLowerCase))
	writef("%s",x);

prints "ace"

Making custom foreachable iterators is not a problem in D. It is custom single-step iterators that I havn't found a neat solution for yet. I.e., as Sean says, being able to iterate through two containers sequentially.

The above versions do use a (as you call it) "dummy" struct that contains an opApply. If function-escaping delegates were implemented, I don't think even the "dummy" struct would be needed, but I don't agree that having a

struct ReverseIterator(T:T[]) {... mixin opApplyImpl; }

is that much of a hack.

/Oskar
October 18, 2006
Walter Bright wrote:
> 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.

I fully agree.

> 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.

Exactly. Making improvements to the language that generalizes common idioms, tremendously shrinks library code or gives additional expressive power is good. But foreach_reverse does not generalize anything or give any additional expressive power. With the boost analogy, it would just replace one single algorithm with a built in equivalent, not reduce the amount of scaffolding all the rest of boost needs.

One example of a generalizing addition is Tomasz' suggestion for trailing delegates. It generalizes foreach, foreach_reverse and even the while-loop. Such an addition would not only make libraries simpler and more powerful. It could also simplify the core language.

What is design if not the pursuit of simplicity?

/Oskar
October 18, 2006
Oskar Linde wrote:

> Walter Bright wrote:
>> 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.
> 
> I fully agree.
> 
>> 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.
> 
> Exactly. Making improvements to the language that generalizes common idioms, tremendously shrinks library code or gives additional expressive power is good. But foreach_reverse does not generalize anything or give any additional expressive power. With the boost analogy, it would just replace one single algorithm with a built in equivalent, not reduce the amount of scaffolding all the rest of boost needs.
> 
> One example of a generalizing addition is Tomasz' suggestion for trailing delegates. It generalizes foreach, foreach_reverse and even the while-loop. Such an addition would not only make libraries simpler and more powerful. It could also simplify the core language.
> 
> What is design if not the pursuit of simplicity?
> 
> /Oskar

Ah, just what I tried to say somewhere else, just not so good at eloquently getting my points across when the ideas get too smart ;)

-- 
Lars Ivar Igesund
blog at http://larsivi.net
DSource & #D: larsivi
October 18, 2006
Tom S wrote:
> Lars Ivar Igesund wrote:
>> I'm just trying to play the devil's (that's Tom) advocate here ;) What I
>> think was shown by Tom, wasn't that the new feature isn't damn useful, but
>> that there might be better, more flexible and more powerful ways to
>> implement the feature, if it is possible with templates, then the sugar
>> shouldn't be any worse, and the possibilities one could gain for D in
>> general could be substantial.
> 
> 
> Thanks, Lars :) Indeed, I'm not saying that the feature is superfluous, I'm just trying to find a better way. For instance, the older 'trailing delegate' proposal could make foreach obsolete by allowing totally custom foreach-alike loops to be created. Last time I mentioned it, Walter seemed to like the proposal, but there was one open problem - namely that if I had code like:
> 

If there is another, even more general way than foreach-on-delegates, it may or may not, be better than foreach-on-delegates and make it superfluous. But as D currently stands, foreach-on-delegates is not superfluous, or less simple to the language. I find the otherwise: it is quite natural and better than using mixins hacks (even if the difference isn't that much). I've favored this feature ever since Oskar Linde(I think it was him) made some posts about using different kinds of iterators in foreach(using that wrapper hack), and the idea that the language itself could support it, like it does now.
But as for foreach_reverse, I do agree that it is very superfluous and way, way specific.



-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D
October 18, 2006
Walter Bright wrote:
> Bruno Medeiros wrote:
>> Walter Bright wrote:
>>> Added foreach_reverse, which addresses a serious shortcoming.
>>>
>>> http://www.digitalmars.com/d/changelog.html
>>
>> foreach_reverse addresses a serious shortcoming? What is that, if instead of:
>>   foreach_reverse(Foo f; aggregate) { ...
>> I can do:
>>   foreach(Foo f; &aggregate.opApplyReverse) { ...
>>
>> The latter form is both more general (allows any kind of iterators) and more simple/orthogonal (no extra special statements are needed).
> 
> The latter form works now.

I know it does (I said "can do" instead of "could do" :P ). So my question remains.

PS: Note, for better readability, one can use a different (and better) delegate name:
  foreach(Foo f; &aggregate.iterReverse) { ...

-- 
Bruno Medeiros - MSc in CS/E student
http://www.prowiki.org/wiki4d/wiki.cgi?BrunoMedeiros#D