Thread overview
Delegate function access to classes local variable
Nov 08, 2013
Colin Grogan
Nov 08, 2013
Dicebot
Nov 08, 2013
Colin Grogan
Nov 08, 2013
Timon Gehr
Nov 08, 2013
Colin Grogan
Nov 10, 2013
Colin Grogan
Nov 10, 2013
Timon Gehr
November 08, 2013
Hi folks,

I'm having some issue getting a delegate function access to a classes member variable.

At object construct time, I'm passing in a delegate function, and a list of parameters after.
The parameters are saved to a variable called vars.
Should I then not be able to access that vars variable from inside my delegate function?

I guess some code is a better explanation:
import std.stdio;
void main()
{
	Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
	Column!string col2 = new Column!string( {return vars[0]; }, "World"); // Compilation fail!! Delegate cant see vars[0]
	writef("%s", col1.nextValue);
	writefln("%s", col2.nextValue);
// I want it to print "Hello, World" here
}

public class Column(Vars...){
	private Vars vars;
	public string delegate() func;
	
	public this(string delegate() func, Vars vars){
		this.vars = vars;
		this.func = func;
	}
	
	public string nextValue(){
		return this.func();
	}
}


The compilation error is:
source/app.d(5): Error: undefined identifier vars

This has been wrecking my head for a couple days now, I'm half way resigned to the fact it cant work but said I'd ask here to be sure.

Thanks!
November 08, 2013
On Friday, 8 November 2013 at 12:43:37 UTC, Colin Grogan wrote:
> import std.stdio;
> void main()
> {
> 	Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
> 	Column!string col2 = new Column!string( {return vars[0]; }, "World"); // Compilation fail!! Delegate cant see vars[0]
> 	writef("%s", col1.nextValue);
> 	writefln("%s", col2.nextValue);
> // I want it to print "Hello, World" here
> }

Delegate refers to context it was created in. Your delegates are created in `main()` scope/context and thus can only access its stack frame. You can't access caller context from delegates.
November 08, 2013
On 11/08/2013 01:43 PM, Colin Grogan wrote:
> Hi folks,
>
> I'm having some issue getting a delegate function access to a classes
> member variable.
>
> At object construct time, I'm passing in a delegate function, and a list
> of parameters after.
> The parameters are saved to a variable called vars.
> Should I then not be able to access that vars variable from inside my
> delegate function?
>
> I guess some code is a better explanation:
> import std.stdio;
> void main()
> {
>      Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
>      Column!string col2 = new Column!string( {return vars[0]; },
> "World"); // Compilation fail!! Delegate cant see vars[0]

It is not even in scope here.

>      writef("%s", col1.nextValue);
>      writefln("%s", col2.nextValue);
> // I want it to print "Hello, World" here
> }
>
> public class Column(Vars...){
>      private Vars vars;
>      public string delegate() func;
>
>      public this(string delegate() func, Vars vars){
>          this.vars = vars;
>          this.func = func;
>      }
>
>      public string nextValue(){
>          return this.func();
>      }
> }
>
>
> The compilation error is:
> source/app.d(5): Error: undefined identifier vars
>
> This has been wrecking my head for a couple days now, I'm half way
> resigned to the fact it cant work but said I'd ask here to be sure.
>
> Thanks!

import std.stdio;
void main(){
    Column!string col1 = new Column!string((ref m)=>"Hello, ", "test");
    Column!string col2 = new Column!string((ref m)=>m.vars[0], "World");
    writef("%s", col1.nextValue);
    writefln("%s", col2.nextValue);
}

public class Column(Vars...){
    struct Members{ Vars vars; }
    private Members members;
    alias members this;
    string delegate(ref Members) func;

    this(string delegate(ref Members) func, Vars vars){
        this.vars = vars;
        this.func = func;
    }

    string nextValue(){
        return func(members);
    }
}

November 08, 2013
On Friday, 8 November 2013 at 13:10:10 UTC, Dicebot wrote:
> On Friday, 8 November 2013 at 12:43:37 UTC, Colin Grogan wrote:
>> import std.stdio;
>> void main()
>> {
>> 	Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
>> 	Column!string col2 = new Column!string( {return vars[0]; }, "World"); // Compilation fail!! Delegate cant see vars[0]
>> 	writef("%s", col1.nextValue);
>> 	writefln("%s", col2.nextValue);
>> // I want it to print "Hello, World" here
>> }
>
> Delegate refers to context it was created in. Your delegates are created in `main()` scope/context and thus can only access its stack frame. You can't access caller context from delegates.

Ok, that clarifies that then :(

Is there any alternative to using delegates for this behaviour? i.e. passing a function at object construct time, and let that function have access to the objects members?
November 08, 2013
On Friday, 8 November 2013 at 13:14:33 UTC, Timon Gehr wrote:
> On 11/08/2013 01:43 PM, Colin Grogan wrote:
>> Hi folks,
>>
>> I'm having some issue getting a delegate function access to a classes
>> member variable.
>>
>> At object construct time, I'm passing in a delegate function, and a list
>> of parameters after.
>> The parameters are saved to a variable called vars.
>> Should I then not be able to access that vars variable from inside my
>> delegate function?
>>
>> I guess some code is a better explanation:
>> import std.stdio;
>> void main()
>> {
>>     Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
>>     Column!string col2 = new Column!string( {return vars[0]; },
>> "World"); // Compilation fail!! Delegate cant see vars[0]
>
> It is not even in scope here.
>
>>     writef("%s", col1.nextValue);
>>     writefln("%s", col2.nextValue);
>> // I want it to print "Hello, World" here
>> }
>>
>> public class Column(Vars...){
>>     private Vars vars;
>>     public string delegate() func;
>>
>>     public this(string delegate() func, Vars vars){
>>         this.vars = vars;
>>         this.func = func;
>>     }
>>
>>     public string nextValue(){
>>         return this.func();
>>     }
>> }
>>
>>
>> The compilation error is:
>> source/app.d(5): Error: undefined identifier vars
>>
>> This has been wrecking my head for a couple days now, I'm half way
>> resigned to the fact it cant work but said I'd ask here to be sure.
>>
>> Thanks!
>
> import std.stdio;
> void main(){
>     Column!string col1 = new Column!string((ref m)=>"Hello, ", "test");
>     Column!string col2 = new Column!string((ref m)=>m.vars[0], "World");
>     writef("%s", col1.nextValue);
>     writefln("%s", col2.nextValue);
> }
>
> public class Column(Vars...){
>     struct Members{ Vars vars; }
>     private Members members;
>     alias members this;
>     string delegate(ref Members) func;
>
>     this(string delegate(ref Members) func, Vars vars){
>         this.vars = vars;
>         this.func = func;
>     }
>
>     string nextValue(){
>         return func(members);
>     }
> }


Ah, brilliant! I like that construct.
Thank you!
November 10, 2013
On Friday, 8 November 2013 at 13:19:05 UTC, Colin Grogan wrote:
> On Friday, 8 November 2013 at 13:14:33 UTC, Timon Gehr wrote:
>> On 11/08/2013 01:43 PM, Colin Grogan wrote:
>>> Hi folks,
>>>
>>> I'm having some issue getting a delegate function access to a classes
>>> member variable.
>>>
>>> At object construct time, I'm passing in a delegate function, and a list
>>> of parameters after.
>>> The parameters are saved to a variable called vars.
>>> Should I then not be able to access that vars variable from inside my
>>> delegate function?
>>>
>>> I guess some code is a better explanation:
>>> import std.stdio;
>>> void main()
>>> {
>>>    Column!string col1 = new Column!string( {return "test"; }, "Hello, ");
>>>    Column!string col2 = new Column!string( {return vars[0]; },
>>> "World"); // Compilation fail!! Delegate cant see vars[0]
>>
>> It is not even in scope here.
>>
>>>    writef("%s", col1.nextValue);
>>>    writefln("%s", col2.nextValue);
>>> // I want it to print "Hello, World" here
>>> }
>>>
>>> public class Column(Vars...){
>>>    private Vars vars;
>>>    public string delegate() func;
>>>
>>>    public this(string delegate() func, Vars vars){
>>>        this.vars = vars;
>>>        this.func = func;
>>>    }
>>>
>>>    public string nextValue(){
>>>        return this.func();
>>>    }
>>> }
>>>
>>>
>>> The compilation error is:
>>> source/app.d(5): Error: undefined identifier vars
>>>
>>> This has been wrecking my head for a couple days now, I'm half way
>>> resigned to the fact it cant work but said I'd ask here to be sure.
>>>
>>> Thanks!
>>
>> import std.stdio;
>> void main(){
>>    Column!string col1 = new Column!string((ref m)=>"Hello, ", "test");
>>    Column!string col2 = new Column!string((ref m)=>m.vars[0], "World");
>>    writef("%s", col1.nextValue);
>>    writefln("%s", col2.nextValue);
>> }
>>
>> public class Column(Vars...){
>>    struct Members{ Vars vars; }
>>    private Members members;
>>    alias members this;
>>    string delegate(ref Members) func;
>>
>>    this(string delegate(ref Members) func, Vars vars){
>>        this.vars = vars;
>>        this.func = func;
>>    }
>>
>>    string nextValue(){
>>        return func(members);
>>    }
>> }
>
>
> Ah, brilliant! I like that construct.
> Thank you!

My optimism may have been premature.
After trying this out today, I havent been able to pass in anything more complex than a 1 line to the constructor.

For example,
    Column!(int, int) randonNumberColumn = new Column!(int, int)((ref m)=>to!string(uniform(m.vars[0], m.vars[1])), 1, 10);

will work.
However,
    Column!(int, int) incrementalNumberColumn = new Column!(int, int)((ref m)=>{m.vars[0]+=m.vars[1]; return to!string(m.vars[0]-m.vars[1]);}, 1,2);

wont.

Maybe my syntax is just wrong or this is simply a limitation?

Also, if you could explain what the => operator is doing there that would be great. I couldnt find the info on it in the docs...
November 10, 2013
On 11/10/2013 09:03 PM, Colin Grogan wrote:
> For example,
>      Column!(int, int) randonNumberColumn = new Column!(int, int)((ref
> m)=>to!string(uniform(m.vars[0], m.vars[1])), 1, 10);
>
> will work.
> However,
>      Column!(int, int) incrementalNumberColumn = new Column!(int,
> int)((ref m)=>{m.vars[0]+=m.vars[1]; return
> to!string(m.vars[0]-m.vars[1]);}, 1,2);
>
> wont.
>
> Maybe my syntax is just wrong or this is simply a limitation?
>

This will work:

Column!(int, int) incrementalNumberColumn = new Column!(int, int)((ref m){m.vars[0]+=m.vars[1];return to!string(m.vars[0]-m.vars[1]);}, 1,2);


> Also, if you could explain what the => operator is doing there that
> would be great. I couldnt find the info on it in the docs...

(ref m)=>exp

is the same as

(ref m){ return exp; }

http://dlang.org/expression.html#Lambda


(hence

(ref m)=>{return exp; }

which is the same as

(ref m)=>(){ return exp; }

is the same as

(ref m)=>()=>exp

the return type of this expression is 'string delegate()' instead of string and therefore the compiler rejects your code.)