www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Delegates and values captured inside loops

reply Anonymouse <zorael gmail.com> writes:
I remember reading this was an issue and now I ran into it myself.

```d
import std.stdio;

void main()
{
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
}
```

Expected output: `foo`, `bar`, `baz`
Actual output:   `baz`, `baz`, `baz`

If I make `names` an `AliasSeq` it works, but I need it to be a 
runtime array.

Is there a workaround?
Jan 20
next sibling parent monkyyy <crazymonkyyy gmail.com> writes:
On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`

 If I make `names` an `AliasSeq` it works, but I need it to be a 
 runtime array.

 Is there a workaround?
Everything is sucks, if your throwing out compile time functions rewriting it as a struct (or generating a struct) is probaly you best bet ```d struct dg{ string s; void opApply(){ s.writeln; } } ```
Jan 20
prev sibling next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`

 If I make `names` an `AliasSeq` it works, but I need it to be a 
 runtime array.

 Is there a workaround?
``` foreach (name; names) { dgs ~= ((name) => () => writeln(name))(name); } ``` lol
Jan 20
parent Anonymouse <zorael gmail.com> writes:
On Saturday, 20 January 2024 at 16:32:42 UTC, FeepingCreature 
wrote:
 ```
 foreach (name; names)
 {
     dgs ~= ((name) => () => writeln(name))(name);
 }
 ```
 lol
Thanks, I'll try that.
Jan 21
prev sibling next sibling parent reply ryuukk_ <ryuukk.dev gmail.com> writes:
On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`

 If I make `names` an `AliasSeq` it works, but I need it to be a 
 runtime array.

 Is there a workaround?
```d import std.stdio; void main() { auto names = [ "foo", "bar", "baz" ]; void delegate()[] dgs; foreach (name; names) { (it) { dgs ~= () => writeln(it); }(name); } foreach (dg; dgs) { dg(); } } ``` This is the workaround according to: https://issues.dlang.org/show_bug.cgi?id=21929#c9
Jan 20
parent reply Renato <renato athaydes.com> writes:
On Saturday, 20 January 2024 at 16:53:12 UTC, ryuukk_ wrote:
 This is the workaround according to: 
 https://issues.dlang.org/show_bug.cgi?id=21929#c9
Go used to have the same issue [but they fixed it](https://go.dev/blog/loopvar-preview) so this is no longer a problem in Go. Perhaps D could do something about it for the same reasons the Go blog post presented.
Jan 21
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On Sunday, 21 January 2024 at 14:52:45 UTC, Renato wrote:
 On Saturday, 20 January 2024 at 16:53:12 UTC, ryuukk_ wrote:
 This is the workaround according to: 
 https://issues.dlang.org/show_bug.cgi?id=21929#c9
Go used to have the same issue [but they fixed it](https://go.dev/blog/loopvar-preview) so this is no longer a problem in Go. Perhaps D could do something about it for the same reasons the Go blog post presented.
Actually, D is much worse. It appears in that post that local variables in the loop were scoped on the loop iteration, but just not the iteration variables themselves. This means, the machinery to properly capture the loop variables was trivial, just change the scope where those variables are allocated. In D, there is no loop scope. So the compiler would have to establish a new mechanism to recognize which variables to stick into a closure. It's not impossible, but it is not the same scope as what Go had to do. -Steve
Jan 21
prev sibling parent reply An Pham <home home.com> writes:
On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`

 If I make `names` an `AliasSeq` it works, but I need it to be a 
 runtime array.

 Is there a workaround?
It is broken by design and the upper afraid to fix it because of broken backward compatible. it Happy coding
Jan 21
next sibling parent reply atzensepp <webwicht web.de> writes:
On Sunday, 21 January 2024 at 20:13:38 UTC, An Pham wrote:
 On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`

 If I make `names` an `AliasSeq` it works, but I need it to be 
 a runtime array.

 Is there a workaround?
It is broken by design and the upper afraid to fix it because of broken backward compatible. fixed it Happy coding
Very weird scoping. The functional programmer's nightmare: ```d import std.stdio; void main() { auto names = [ "foo", "bar", "baz" ]; void delegate(int)[] dgs; string myname="Original "; foreach (name; names) { int j=0; void delegate(int) lambdaFunction = (int i) { writeln("N:",name," ",myname, j++," ",i); // ok }; j=100; dgs ~= lambdaFunction; } int i=0; foreach (dg; dgs) { dg(i++); myname="Side effect "; } } ``` ``` ./a.out N:baz Original 100 0 N:baz Side effect 101 1 N:baz Side effect 102 2 ``` Good: the function is indeed invoked. Why the output starts with 100 and not 0? Where "lives" variable j which is declared in the scope of the foreach-block?
Jan 21
parent atzensepp <webwicht web.de> writes:
In Ocaml this would look as:
```d
let names =["foo";"bar";"baz"] in
let funmap = List.map (  fun name -> ( fun () -> print_endline 
name)) names in
List.iter ( fun f -> f () ) funmap
;;
~
```
I think in the D-example it is basically one function and not 3.
Jan 21
prev sibling parent An Pham <home home.com> writes:
On Sunday, 21 January 2024 at 20:13:38 UTC, An Pham wrote:
 On Saturday, 20 January 2024 at 15:59:59 UTC, Anonymouse wrote:
 I remember reading this was an issue and now I ran into it 
 myself.

 ```d
 import std.stdio;

 void main()
 {
     auto names = [ "foo", "bar", "baz" ];
     void delegate()[] dgs;

     foreach (name; names)
     {
         dgs ~= () => writeln(name);
     }

     foreach (dg; dgs)
     {
         dg();
     }
 }
 ```

 Expected output: `foo`, `bar`, `baz`
 Actual output:   `baz`, `baz`, `baz`
https://stackoverflow.com/questions/3168375/using-the-iterator-variable-of-foreach-loop-in-a-lambda-expression-why-fails To have a way out for old behavior by capture reference, from: dgs ~= () => writeln(name); to: dgs ~= () => writeln(&name);
Jan 21