www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to create delegates with an independent scope?

reply Vijay Nayar <madric gmail.com> writes:
Consider the following code example:

```d
import std.stdio;

void main()
{
   alias DelegateT = string delegate();

   // An array of delegates, each has their own scope.
   DelegateT[] funcs;
   foreach (i; ["ham", "cheese"]) {
     // Deliberately create a copy to keep in delegate scope.
     string myStr = i.dup;
     // The delegate will hopefully carry this copy around in its 
own scope.
     funcs ~= (() => myStr ~ " sandwich");
   }
	
   foreach (f; funcs) {
     writeln(f());
   }
}
```

The expected output is: "ham sandwich" and then "cheese sandwich".

The actual output is: "cheese sandwich" and then "cheese 
sandwich".

It seems that the variable `myStr` is in a sort of shared scope 
for both functions in the array, and the last value written to it 
dominates.

How do I create a delegate that acts like a 
[closure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures),
that is, it carries with it the environment in which it was created?
Mar 30 2022
next sibling parent reply vit <vit vit.vit> writes:
On Wednesday, 30 March 2022 at 12:46:07 UTC, Vijay Nayar wrote:
 Consider the following code example:

 ```d
 import std.stdio;

 void main()
 {
   alias DelegateT = string delegate();

   // An array of delegates, each has their own scope.
   DelegateT[] funcs;
   foreach (i; ["ham", "cheese"]) {
     // Deliberately create a copy to keep in delegate scope.
     string myStr = i.dup;
     // The delegate will hopefully carry this copy around in 
 its own scope.
     funcs ~= (() => myStr ~ " sandwich");
   }
 	
   foreach (f; funcs) {
     writeln(f());
   }
 }
 ```

 The expected output is: "ham sandwich" and then "cheese 
 sandwich".

 The actual output is: "cheese sandwich" and then "cheese 
 sandwich".

 It seems that the variable `myStr` is in a sort of shared scope 
 for both functions in the array, and the last value written to 
 it dominates.

 How do I create a delegate that acts like a 
 [closure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures),
that is, it carries with it the environment in which it was created?
use two delegates :) ```d import std; void main(){ alias DelegateT = string delegate(); // An array of delegates, each has their own scope. DelegateT[] funcs; foreach (i; ["ham", "cheese"]) { (){ // Deliberately create a copy to keep in delegate scope. string myStr = i.dup; // The delegate will hopefully carry this copy around in its own scope. funcs ~= (() => myStr ~ " sandwich"); }(); } foreach (f; funcs) { writeln(f()); } } ```
Mar 30 2022
parent reply Vijay Nayar <madric gmail.com> writes:
On Wednesday, 30 March 2022 at 12:53:10 UTC, vit wrote:
 use two delegates :)

 ```d
         (){
 			// Deliberately create a copy to keep in delegate scope.
 			string myStr = i.dup;
 			// The delegate will hopefully carry this copy around in its 
 own scope.
 			funcs ~= (() => myStr ~ " sandwich");
         }();
 ```
Very interesting. Both this and creating a "function creator function" work, and it seems clear that functions create their own scopes. However, it seems that loops do not, is that correct? Maybe I was thrown off by the surrounding `{ }`, but I had assumed that loops created their own scopes.
Mar 30 2022
parent vit <vit vit.vit> writes:
On Wednesday, 30 March 2022 at 12:56:39 UTC, Vijay Nayar wrote:
 On Wednesday, 30 March 2022 at 12:53:10 UTC, vit wrote:
 use two delegates :)

 ```d
         (){
 			// Deliberately create a copy to keep in delegate scope.
 			string myStr = i.dup;
 			// The delegate will hopefully carry this copy around in 
 its own scope.
 			funcs ~= (() => myStr ~ " sandwich");
         }();
 ```
Very interesting. Both this and creating a "function creator function" work, and it seems clear that functions create their own scopes. However, it seems that loops do not, is that correct? Maybe I was thrown off by the surrounding `{ }`, but I had assumed that loops created their own scopes.
It is bug: https://issues.dlang.org/show_bug.cgi?id=21929
Mar 30 2022
prev sibling parent Tejas <notrealemail gmail.com> writes:
On Wednesday, 30 March 2022 at 12:46:07 UTC, Vijay Nayar wrote:
 Consider the following code example:

 ```d
 import std.stdio;

 void main()
 {
   alias DelegateT = string delegate();

   // An array of delegates, each has their own scope.
   DelegateT[] funcs;
   foreach (i; ["ham", "cheese"]) {
     // Deliberately create a copy to keep in delegate scope.
     string myStr = i.dup;
     // The delegate will hopefully carry this copy around in 
 its own scope.
     funcs ~= (() => myStr ~ " sandwich");
   }
 	
   foreach (f; funcs) {
     writeln(f());
   }
 }
 ```

 The expected output is: "ham sandwich" and then "cheese 
 sandwich".

 The actual output is: "cheese sandwich" and then "cheese 
 sandwich".

 It seems that the variable `myStr` is in a sort of shared scope 
 for both functions in the array, and the last value written to 
 it dominates.

 How do I create a delegate that acts like a 
 [closure](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures),
that is, it carries with it the environment in which it was created?
You can also use `static foreach` and `alias`, although that will restrict that code to compile time usage only :/ ```d import std; import std.stdio; void main() { alias DelegateT = string delegate(); // An array of delegates, each has their own scope. DelegateT[] funcs; static foreach (alias i; ["ham", "cheese"]) { // Deliberately create a copy to keep in delegate scope. //string myStr = i.dup; // The delegate will hopefully carry this copy around in its own scope. funcs ~= (() => /*myStr*/ i ~ " sandwich"); } foreach (f; funcs) { writeln(f()); } } ```
Mar 30 2022