Thread overview
How to create delegates with an independent scope?
Mar 30, 2022
Vijay Nayar
Mar 30, 2022
vit
Mar 30, 2022
Vijay Nayar
Mar 30, 2022
vit
Mar 30, 2022
Tejas
March 30, 2022

Consider the following code example:

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, that is, it carries with it the environment in which it was created?

March 30, 2022

On Wednesday, 30 March 2022 at 12:46:07 UTC, Vijay Nayar wrote:

>

Consider the following code example:

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, that is, it carries with it the environment in which it was created?

use two delegates :)

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());
	}
}
March 30, 2022

On Wednesday, 30 March 2022 at 12:53:10 UTC, vit wrote:

>

use two delegates :)

        (){
			// 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.

March 30, 2022

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 :)

        (){
			// 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

March 30, 2022

On Wednesday, 30 March 2022 at 12:46:07 UTC, Vijay Nayar wrote:

>

Consider the following code example:

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, 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 :/

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());
  }
}