www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - D2 full closure?

reply Frank Benoit <keinfarbton googlemail.com> writes:
With DMD 2.015:

alias void delegate() Runner;
void main(){
   Runner[] runner;
   for( int i = 0; i < 3; i++ ){
     const int ci = i;
     runners ~= delegate(){
       writefln( "run with ci=%d", ci );
     }
   }
   foreach( runner; runners ){
     runner();
   }
}


Output:
run with ci=2
run with ci=2
run with ci=2

Is this output expected?
 From the technical point yes, because i know how it is implemented.
But is this expected in the sense of "Full closure"?

In Java, if ci would be "final" and the delegate an anonymous class,
the output would be 0,1,2.
Jul 13 2008
next sibling parent reply Jason House <jason.james.house gmail.com> writes:
Frank Benoit Wrote:

 With DMD 2.015:
 
 alias void delegate() Runner;
 void main(){
    Runner[] runner;
    for( int i = 0; i < 3; i++ ){
      const int ci = i;
      runners ~= delegate(){
        writefln( "run with ci=%d", ci );
      }
    }
    foreach( runner; runners ){
      runner();
    }
 }
 
 
 Output:
 run with ci=2
 run with ci=2
 run with ci=2
 
 Is this output expected?
  From the technical point yes, because i know how it is implemented.
 But is this expected in the sense of "Full closure"?
 
 In Java, if ci would be "final" and the delegate an anonymous class,
 the output would be 0,1,2.

I could have sworn there was a bug report to make this work like you want. Another alternative is to use bind.
Jul 13 2008
parent reply Frank Benoit <keinfarbton googlemail.com> writes:
Jason House schrieb:
 Frank Benoit Wrote:
 
 With DMD 2.015:

 alias void delegate() Runner;
 void main(){
    Runner[] runner;
    for( int i = 0; i < 3; i++ ){
      const int ci = i;
      runners ~= delegate(){
        writefln( "run with ci=%d", ci );
      }
    }
    foreach( runner; runners ){
      runner();
    }
 }


 Output:
 run with ci=2
 run with ci=2
 run with ci=2

 Is this output expected?
  From the technical point yes, because i know how it is implemented.
 But is this expected in the sense of "Full closure"?

 In Java, if ci would be "final" and the delegate an anonymous class,
 the output would be 0,1,2.

I could have sworn there was a bug report to make this work like you want. Another alternative is to use bind.

Yes i know there are ways to make it work. But my question is, shouldn't it work in this code? Is it a bug or not? Is it ok that the constant is changing the value? Should each delegate get its own heap allocated copy of the stack frame?
Jul 14 2008
parent Jason House <jason.james.house gmail.com> writes:
Frank Benoit wrote:

 Jason House schrieb:
 Frank Benoit Wrote:
 
 With DMD 2.015:

 alias void delegate() Runner;
 void main(){
    Runner[] runner;
    for( int i = 0; i < 3; i++ ){
      const int ci = i;
      runners ~= delegate(){
        writefln( "run with ci=%d", ci );
      }
    }
    foreach( runner; runners ){
      runner();
    }
 }


 Output:
 run with ci=2
 run with ci=2
 run with ci=2

 Is this output expected?
  From the technical point yes, because i know how it is implemented.
 But is this expected in the sense of "Full closure"?

 In Java, if ci would be "final" and the delegate an anonymous class,
 the output would be 0,1,2.

I could have sworn there was a bug report to make this work like you want. Another alternative is to use bind.

Yes i know there are ways to make it work. But my question is, shouldn't it work in this code? Is it a bug or not? Is it ok that the constant is changing the value? Should each delegate get its own heap allocated copy of the stack frame?

If none of the D language authors reply in this thread or post to the bug report, we'll never know what was intended.
Jul 14 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Frank Benoit wrote:
 Is this output expected?

No, it's a bug. The output should be 0,1,2. Is it on bugzilla?
Jul 15 2008
next sibling parent Frank Benoit <keinfarbton googlemail.com> writes:
Walter Bright schrieb:
 Frank Benoit wrote:
 Is this output expected?

No, it's a bug. The output should be 0,1,2. Is it on bugzilla?

ok, now it is. (#2228)
Jul 15 2008
prev sibling parent reply BCS <ao pathlink.com> writes:
Reply to Walter,

 Frank Benoit wrote:
 
 Is this output expected?
 


What?!! The value for the delegates are copied at the point of the '&'? That seems more like currying to me. In general I the behavior I would expect is that the values would never be copied. I would expect that the outer function would store some of it's variable on the heap and then all access (via delegate or in the outer function) would be to the same values. Why is the reverse preferable? the proposed-to-be-correct behavior precludes having more than one delegate operate on the same stuff or more generally, have side effects on the outer function variables: int delegate() DoIt() { int j = 10; int i = 0 int B(){return i+=4;} auto dg = &B; while(j) { dg(); if(j & 0x01) i--; } return dg; }
Jul 15 2008
next sibling parent downs <default_357-line yahoo.de> writes:
BCS wrote:
 Reply to Walter,
 
 Frank Benoit wrote:

 Is this output expected?


What?!! The value for the delegates are copied at the point of the '&'? That seems more like currying to me.

No, the problem is something else. Currently, closures are apparently formed with the function body. However, in this case, it is reasonable to expect them to be instead formed with *the surrounding scope*, especially since, in the case of loops, it sort of behaves like a function being called anyway. (like a half-way cross between scope and function). --downs
Jul 15 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
BCS wrote:
 Reply to Walter,
 
 Frank Benoit wrote:

 Is this output expected?


What?!! The value for the delegates are copied at the point of the '&'?

No, they are never copied. The delegates should refer directly to the variable which, in the case of a closure, gets allocated on the heap rather than the stack frame.
 
 That seems more like currying to me.
 
 In general I the behavior I would expect is that the values would never 
 be copied. I would expect that the outer function would store some of 
 it's variable on the heap and then all access (via delegate or in the 
 outer function) would be to the same values.
 
 Why is the reverse preferable?

It isn't. The behavior you expect *is* the current behavior. The particular const behavior is (I haven't delved into it) the compiler incorrectly deciding that a const variable wouldn't need to be put on the heap.
Jul 15 2008
parent reply BCS <ao pathlink.com> writes:
Reply to Walter,

 BCS wrote:
 
 Reply to Walter,
 
 Frank Benoit wrote:
 
 Is this output expected?
 


'&'?

variable which, in the case of a closure, gets allocated on the heap rather than the stack frame.

Oh crud, I read it wrong. OTOH that problem is even worse than the issue I was thinking of: is this valid? does it generate a different heap frame for each time through the loop? alias void delegate() Runner; void main(){ Runner[] runner; int i; for(i = 0; i < 3; i++ ){ const int ci = i; runners ~= delegate(){ writefln( "run with ci=%d, i=%d", ci, i ); } } foreach( runner; runners ){ runner(); } } is this valid? void main() { Runner a; { int i = 5; a = {i++;} } { int j = 6; a(); assert(j==6); } }
Jul 15 2008
parent reply Walter Bright <newshound1 digitalmars.com> writes:
BCS wrote:
 Oh crud, I read it wrong.
 
 OTOH that problem is even worse than the issue I was thinking of:
 
 is this valid?

Yes.
 does it generate a different heap frame for each time 
 through the loop?

All that happens is the stack frame is put on the heap. It doesn't allocate new heap frames in a loop.
 
 alias void delegate() Runner;
 
 void main(){
  Runner[] runner;
  int i;
  for(i = 0; i < 3; i++ ){
    const int ci = i;
    runners ~= delegate(){ writefln( "run with ci=%d, i=%d", ci, i ); }
  }
  foreach( runner; runners ){
    runner();
  }
 }
 
 
 is this valid?

Yes.
 
 void main()
 {
  Runner a;
  {
      int i = 5;
      a = {i++;}
  }
 
  {
      int j = 6;
      a();
      assert(j==6);
  }
 }
 
 

Jul 15 2008
parent BCS <ao pathlink.com> writes:
Reply to Walter,

 BCS wrote:
 
 Oh crud, I read it wrong.
 
 OTOH that problem is even worse than the issue I was thinking of:
 
 is this valid?
 

 does it generate a different heap frame for each time through the
 loop?
 

allocate new heap frames in a loop.

Well in that cases why would it be expected that the const value be frozen? Each time through the loop the value is stored on that stack in the same place. At then end of the loop, all the delegates point print out a value from the same location.
 alias void delegate() Runner;
 
 void main(){
 Runner[] runner;
 int i;
 for(i = 0; i < 3; i++ ){
 const int ci = i;
 runners ~= delegate(){ writefln( "run with ci=%d, i=%d", ci, i ); }
 }
 foreach( runner; runners ){
 runner();
 }
 }
 is this valid?
 

Yes.

Couldn't i and j overlap?
 void main()
 {
 Runner a;
 {
 int i = 5;
 a = {i++;}
 }
 {
 int j = 6;
 a();
 assert(j==6);
 }
 }


Jul 16 2008