confusing (buggy?) closure behaviour

Zoran Isailovski dmd.zoc at spamgourmet.com
Fri Dec 12 12:24:39 PST 2008


Denis Koroskin Wrote:

> On Fri, 12 Dec 2008 22:28:01 +0300, Zoran Isailovski  
> <dmd.zoc at spamgourmet.com> wrote:
> 
> > Denis Koroskin Wrote:
> >
> >> On Fri, 12 Dec 2008 19:32:03 +0300, Zoran Isailovski
> >> <dmd.zoc at spamgourmet.com> wrote:
> >>
> >> > I'm an experienced C#, Java and Python programmer, and have employed
> >> > closures (and C# delegates) upon numerous occasions. While  
> >> experimenting
> >> > with D closures and delegates, I was stroke by a phenomenon I cannot
> >> > explain. Here's the code:
> >> >
> >> > module closures01;
> >> >
> >> > import std.stdio;
> >> >
> >> > alias int delegate(int arg) Handler;
> >> >
> >> > Handler incBy(int n)
> >> > {
> >> > 	return delegate(int arg){ return arg + n; };
> >> > }
> >> >
> >> > Handler mulBy(int n)
> >> > {
> >> > 	return delegate(int arg){ return arg * n; };
> >> > }
> >> >
> >> > void test1()
> >> > {
> >> > 	writefln("\ntest1:\n----------------------------------------");
> >> > 	int x = 10, y;
> >> > 	y = mulBy(3)(x); writefln("%d * 3 -> %d", x, y);
> >> > 	y = mulBy(4)(x); writefln("%d * 4 -> %d", x, y);
> >> > 	y = incBy(2)(x); writefln("%d + 2 -> %d", x, y);
> >> > }
> >> >
> >> > void test2()
> >> > {
> >> > 	writefln("\ntest2:\n----------------------------------------");
> >> > 	int x = 10, y;
> >> > 	Handler times3 = mulBy(3);
> >> > 	Handler times4 = mulBy(4);
> >> > 	Handler plus2 = incBy(2);
> >> > 	y = times3(x); writefln("%d * 3 -> %d", x, y);
> >> > 	y = times4(x); writefln("%d * 4 -> %d", x, y);
> >> > 	y = plus2(x); writefln("%d + 2 -> %d", x, y);
> >> > }
> >> >
> >> > public void run()
> >> > {
> >> > 	test1();
> >> > 	test2();
> >> > }
> >> >
> >> > /* **************************************** *
> >> >  * Compiled with: Digital Mars D Compiler v1.030
> >> >  *
> >> >  * (Unexplainable) program output:
> >> > test1:
> >> > ----------------------------------------
> >> > 10 * 3 -> 30
> >> > 10 * 4 -> 40
> >> > 10 + 2 -> 12
> >> >
> >> > test2:
> >> > ----------------------------------------
> >> > 10 * 3 -> 20
> >> > 10 * 4 -> 42846880
> >> > 10 + 2 -> 4284698
> >> >
> >> > * **************************************** */
> >> >
> >> > What goes wrong???
> >>
> >> I'd say that it works as expected and here is why.
> >>
> >> First of all, there are two types of closures:  static and dynamic
> >> closures.
> >> Closures work by having a hidden pointer to function frame where all  
> >> local
> >> variables are stored.
> >>
> >> When a static closure is created, all the function local variables are
> >> stored on stack.
> >> It has an advantage that no memory allocation takes place (fast).
> >> It has a disadvantage that once the delegate leaves the scope, it  
> >> becomes
> >> invalid since variables were stored on stack and the stack is probably
> >> overwritten (unsafe).
> >>
> >> Dynamic closure allocates memory in a heap and all the local variables  
> >> are
> >> placed there.
> >> It has a disadvantage that memory is allocated for dynamic closure  
> >> (might
> >> be slow if dynamic closure are created often).
> >> It has an advantage that dynamic closure may leave the scope, i.e. you  
> >> may
> >> save it and call whenever you want.
> >>
> >> D1 support static closures only! That's why your code doesn't work (in
> >> test1 stack is still valid, but in test2 stack gets overwritten)
> >> D2 has support for dynamic closures. Just try it - your sample works as  
> >> is.
> >
> > An addition:
> >
> > Given the complexness of the criteria when a closure works and when not,  
> > I would vote for a compiler error on inappropriate closures usage. (In  
> > Java, closures cannot handle mutable values on the stack, so it's an  
> > error for a method to return a closure that refers to a non-final  
> > argument.)
> 
> This would restrict their usage so badly that may make then next to  
> useless.
> 
> No worries, a dynamic closure is created automatically whenever a static  
> one it might be unsafe in D2.
> 
> As a rule of thumb, you shoudn't don't return local delegate from a  
> function in D1, but you may safely pass them down the call stack.

I don't think it is restrictive if the compiler prevented a situation that would otherwise lead to a run-time error anyway, or worse, weird and confusing run-time behavior. In my case, if the compiler couldn't SAFELY handle a reference to the argument n outside the enclosing function, then, IMO, RETURNING it (but not otherwise using it) should be flagged a compilation error. Admittedly, detecting this is a bit more involved for the compiler, but not at all restrictive for the user.


More information about the Digitalmars-d-learn mailing list