digitalmars.D - D2 closure and loop variables
- Adam D. Ruppe (36/36) Oct 31 2009 I'm trying to make a delegate thing in a loop and hit something that see...
- Jason House (2/3) Oct 31 2009 I can confirm that your intuition matches others'. I don't have a link, ...
- Robert Jacques (11/62) Oct 31 2009 For me, this makes sense and isn't a bug. A closure should be able to
I'm trying to make a delegate thing in a loop and hit something that seemed intuitively wrong. Here's some short code that shows what I mean: === import std.stdio; class A { this(void delegate() _a) { a = _a; } void run() { a(); } private void delegate() a; } void main() { auto list = ['a', 'b', 'c']; A[string] buttons; foreach(l; list) { buttons[l ~ "\n"] = new A( { writefln("%s", l); } ); } auto a = readln(); buttons[a].run; } === What happens is no matter what key you press, you always get the output of "c", whereas at first glance, I'd expect it to echo back the same letter to you. This makes sense when thinking about it in terms of memory: the delegate accesses the memory that variable l had assigned to it, which was overwritten by later loop iterations. But, while it makes sense when thinking about it, intuitively, I expected that loop variable to be copied over somewhere, so the delegate would be accessing its own private copy, not the pointer being overwritten by the loop. Generally, if a delegate escapes any scope, I expect it to take a snapshot of the stack it is referencing at that time to make its private copy at the time it passes the closing brace. What I'm asking is: a) Is my expectation wrong, or does that make sense? b) If not wrong, should D be doing this now, or is this a bug? and c) If it isn't a bug, should it be? Copying it on every loop iteration would have a definite performance penalty, so it isn't remotely free. Moreover, being able to reference variables that might change later can be likely a good thing: [code] SomeClass a; foreach(item; collection) item.someDelegate = { a.doSomething;} a = new SomeClass; [/code] If it make a private copy inside that loop, someDelegate would be looking at null the whole time, whereas with the current behaviour, this code works. So changing it might not even be sane from that perspective. What do you guys think?
Oct 31 2009
Adam D. Ruppe Wrote:I'm trying to make a delegate thing in a loop and hit something that seemed intuitively wrong. Here's some short code that shows what I mean:I can confirm that your intuition matches others'. I don't have a link, but I do remember a past discussion about binding where this behavior was discussed.
Oct 31 2009
On Sat, 31 Oct 2009 20:52:05 -0400, Adam D. Ruppe <destructionator gmail.com> wrote:I'm trying to make a delegate thing in a loop and hit something that seemed intuitively wrong. Here's some short code that shows what I mean: === import std.stdio; class A { this(void delegate() _a) { a = _a; } void run() { a(); } private void delegate() a; } void main() { auto list = ['a', 'b', 'c']; A[string] buttons; foreach(l; list) { buttons[l ~ "\n"] = new A( { writefln("%s", l); } ); } auto a = readln(); buttons[a].run; } === What happens is no matter what key you press, you always get the output of "c", whereas at first glance, I'd expect it to echo back the same letter to you. This makes sense when thinking about it in terms of memory: the delegate accesses the memory that variable l had assigned to it, which was overwritten by later loop iterations. But, while it makes sense when thinking about it, intuitively, I expected that loop variable to be copied over somewhere, so the delegate would be accessing its own private copy, not the pointer being overwritten by the loop. Generally, if a delegate escapes any scope, I expect it to take a snapshot of the stack it is referencing at that time to make its private copy at the time it passes the closing brace. What I'm asking is: a) Is my expectation wrong, or does that make sense? b) If not wrong, should D be doing this now, or is this a bug? and c) If it isn't a bug, should it be? Copying it on every loop iteration would have a definite performance penalty, so it isn't remotely free. Moreover, being able to reference variables that might change later can be likely a good thing: [code] SomeClass a; foreach(item; collection) item.someDelegate = { a.doSomething;} a = new SomeClass; [/code] If it make a private copy inside that loop, someDelegate would be looking at null the whole time, whereas with the current behaviour, this code works. So changing it might not even be sane from that perspective. What do you guys think?For me, this makes sense and isn't a bug. A closure should be able to mutate it's enclosing scope and be affected by mutation. That said, a enhancement that allowed variables to copied by value, instead of by reference, might be nice. As for your particular issue, since it looks like you're doing GUI stuff, you might want to take a look at DFL. One of the things it does it to has all it's delegates take an object and an args object. As most of DFL's objects support tags, this allows you to retrieve custom information from the delegate arguments (though casting all the time does get ugly)
Oct 31 2009