digitalmars.D - From stack delegates to true closures
- Søren J. Løvborg (70/70) Jan 31 2006 I'm aware this has been discussed before, but I'd like to bring it up ag...
- Kris (14/86) Jan 31 2006 Why not just do something like this instead (untested) ?
- Søren J. Løvborg (13/23) Feb 02 2006 That is essentially how I imagined true closures to be implemented (exce...
- Sean Kelly (5/8) Jan 31 2006 I just had a crazy vision of a stackless version of D where closures
- John Reimer (10/21) Jan 31 2006 I like the idea, actually. D gives a taste of what is possible, but it
- Walter Bright (2/2) Feb 02 2006 I think static closures are very useful and are a logical step for D (si...
- Søren J. Løvborg (4/6) Feb 04 2006 Great! I'll look forward to their inclusion, in 2.0 or whenever it's rea...
- John Reimer (7/11) Feb 04 2006 Woah! Wait a minute, what's the difference between static closures and
- Walter Bright (3/10) Feb 08 2006 That's my understanding.
- Bruno Medeiros (7/20) Feb 09 2006 Sounds like a very bad name term then. "True" closures imply allocating
- Eugene K. (46/58) Feb 10 2006 It is very useful as I see it. I recently looked at D at found it very p...
I'm aware this has been discussed before, but I'd like to bring it up again
(sorry!): D really should have true closures, or "stack delegates that can
be returned" if you will.
First of all, they're a very useful feature, allowing short and concise
code.
Secondly, they'd be a good D feature to point at whenever faced with the
question, "Oh, but what's D got that Java/C++ hasn't?"
But mainly, because D is already so close to supporting them with stack
delegates (aka dynamic closures).
The problem with stack delegates is that they're only valid as long as one
does not return from the function in which they were created, which is a
rather serious limitation, and (it seems to me) the only thing standing in
the way of true closures.
It's been argued previously that implementing true closures would add a lot
of overhead to the existing stack delegates -- the cost of copying the
entire stackframe has been mentioned as a problem. However, it seems to me
that this can be reduced to the much smaller cost of allocating a block of
memory, having the garbage collector free it later, and an additional level
of indirection.
Contrived example: createFunc returns a stack delegate, which takes an "out"
MyType argument and initializes it to a value calculated at the time
createFunc was run.
void delegate(out MyType) createFunc()
{
MyType foo, bar;
/* do stuff with foo and bar */
return delegate void(out MyType dst) { dst = foo; };
}
This compiles fine, but doens't work, since foo is long gone from the stack
at the time the returned delegate is invoked. In order for it to work, foo
will have to be allocated on the heap, rather than on the stack. (Static
variables aren't thread-safe.) Simply copying the value of foo to a newly
allocated block of memory isn't a good solution, since 1) MyType might be a
very large type (say, int[100000]) and 2) changes to one copy does not
propagate to the other.
It seems to me that these problems can be avoided, however, by never
allocating foo on the stack. In other words, the above code should be
translated into something like
void delegate(out MyType) createFunc()
{
struct _EXT { MyType foo; };
_EXT* _ext = new _EXT;
MyType bar;
/* do stuff with _EXT.foo and bar */
return delegate void(out MyType dst) { dst = _EXT.foo; };
}
where, instead of wrapping the soon-to-be invalid stack pointer in the
delegate, the compiler should wrap the actual _ext pointer, adjusting the
delegate code as necessary.
The delegate runs as fast as ever, and this would prevent any unnecessary
copying, reducing the overhead to that of an allocation (peanuts, I hope),
an extra level of indirection when accessing foo from within createFunc
(which the compiler should be able to optimize away in most cases, as the
_ext pointer can be cached in a register), and the cost of garbage
collecting the _EXT storage.
I guess the GC'ing could cause trouble if/when delegates and function
pointers are merged, and delegates might end up being stored in
non-GC-managed memory (for Windows API callbacks, e.g.) -- however, since
_ext will remain on the stack until the function returns, this new kind of
delegate would still be valid for at least as long as the current kind.
The allocating/GC overhead might prove troublesome if a lot of delegates
were allocated, although one probably shouldn't use delegates in bottleneck
code anyway (in that respect, function calls aren't exactly cheap either).
One option to work around this might be to add new syntax, say "new
delegate" for this new behaviour, if implemented.
Any comments? Am I the only one who'd find this useful? Is there already
similar functionality, and I've just not found it? (If so, please
elaborate!)
Søren J. Løvborg
web kwi.dk
Jan 31 2006
Why not just do something like this instead (untested) ?
void delegate (out MyType) createFunc()
{
class Foo
{
MyType foo;
void func (out MyType dst) {dst = foo;}
}
return & (new Foo).func;
}
I presume the anonymous-class support in D would make that a bit more
concise, but the general strategy should work?
"Søren J. Løvborg" <web kwi.dk> wrote in message
news:drod3j$1jm0$1 digitaldaemon.com...
I'm aware this has been discussed before, but I'd like to bring it up
again (sorry!): D really should have true closures, or "stack delegates
that can be returned" if you will.
First of all, they're a very useful feature, allowing short and concise
code.
Secondly, they'd be a good D feature to point at whenever faced with the
question, "Oh, but what's D got that Java/C++ hasn't?"
But mainly, because D is already so close to supporting them with stack
delegates (aka dynamic closures).
The problem with stack delegates is that they're only valid as long as one
does not return from the function in which they were created, which is a
rather serious limitation, and (it seems to me) the only thing standing in
the way of true closures.
It's been argued previously that implementing true closures would add a
lot of overhead to the existing stack delegates -- the cost of copying the
entire stackframe has been mentioned as a problem. However, it seems to me
that this can be reduced to the much smaller cost of allocating a block of
memory, having the garbage collector free it later, and an additional
level of indirection.
Contrived example: createFunc returns a stack delegate, which takes an
"out" MyType argument and initializes it to a value calculated at the time
createFunc was run.
void delegate(out MyType) createFunc()
{
MyType foo, bar;
/* do stuff with foo and bar */
return delegate void(out MyType dst) { dst = foo; };
}
This compiles fine, but doens't work, since foo is long gone from the
stack at the time the returned delegate is invoked. In order for it to
work, foo will have to be allocated on the heap, rather than on the stack.
(Static variables aren't thread-safe.) Simply copying the value of foo to
a newly allocated block of memory isn't a good solution, since 1) MyType
might be a very large type (say, int[100000]) and 2) changes to one copy
does not propagate to the other.
It seems to me that these problems can be avoided, however, by never
allocating foo on the stack. In other words, the above code should be
translated into something like
void delegate(out MyType) createFunc()
{
struct _EXT { MyType foo; };
_EXT* _ext = new _EXT;
MyType bar;
/* do stuff with _EXT.foo and bar */
return delegate void(out MyType dst) { dst = _EXT.foo; };
}
where, instead of wrapping the soon-to-be invalid stack pointer in the
delegate, the compiler should wrap the actual _ext pointer, adjusting the
delegate code as necessary.
The delegate runs as fast as ever, and this would prevent any unnecessary
copying, reducing the overhead to that of an allocation (peanuts, I hope),
an extra level of indirection when accessing foo from within createFunc
(which the compiler should be able to optimize away in most cases, as the
_ext pointer can be cached in a register), and the cost of garbage
collecting the _EXT storage.
I guess the GC'ing could cause trouble if/when delegates and function
pointers are merged, and delegates might end up being stored in
non-GC-managed memory (for Windows API callbacks, e.g.) -- however, since
_ext will remain on the stack until the function returns, this new kind of
delegate would still be valid for at least as long as the current kind.
The allocating/GC overhead might prove troublesome if a lot of delegates
were allocated, although one probably shouldn't use delegates in
bottleneck code anyway (in that respect, function calls aren't exactly
cheap either).
One option to work around this might be to add new syntax, say "new
delegate" for this new behaviour, if implemented.
Any comments? Am I the only one who'd find this useful? Is there already
similar functionality, and I've just not found it? (If so, please
elaborate!)
Søren J. Løvborg
web kwi.dk
Jan 31 2006
"Kris" <fu bar.com> wrote:
Why not just do something like this instead (untested) ?
void delegate (out MyType) createFunc()
{
class Foo
{
MyType foo;
void func (out MyType dst) {dst = foo;}
}
return & (new Foo).func;
}
That is essentially how I imagined true closures to be implemented (except
for any extra garbage that classes might bring along with them; I don't know
if the current D implementation includes a VMT for classes with neither
parent nor children, for instance).
However, the above code is quite verbose compared to
void delegate (out MyType) createFunc()
{
MyType foo;
return delegate void(out MyType dst) {dst = foo;};
}
Søren J. Løvborg
web kwi.dk
Feb 02 2006
Søren J. Løvborg wrote:I'm aware this has been discussed before, but I'd like to bring it up again (sorry!): D really should have true closures, or "stack delegates that can be returned" if you will.I just had a crazy vision of a stackless version of D where closures simply reference the needed data via a pointer and let the GC worry about cleaning it up. Might be an interesting experiment if nothing else. Sean
Jan 31 2006
Sean Kelly wrote:Søren J. Løvborg wrote:I like the idea, actually. D gives a taste of what is possible, but it doesn't quite go all the way. Once I read up on what real dynamic closures were all about, my imagination was piqued: I realized D really just pretends at dynamic closures. ( The idea of closures seem to have drifted over from the functional languages ). Kris' suggestion would work, of course. But that way doesn't look near as elegant. It looks like a workaround for what should be left to real dynamic closures (sorry, Kris ;) ). But perhaps looks are deceiving. -JJRI'm aware this has been discussed before, but I'd like to bring it up again (sorry!): D really should have true closures, or "stack delegates that can be returned" if you will.I just had a crazy vision of a stackless version of D where closures simply reference the needed data via a pointer and let the GC worry about cleaning it up. Might be an interesting experiment if nothing else. Sean
Jan 31 2006
I think static closures are very useful and are a logical step for D (since they add power without changing syntax at all), just not now.
Feb 02 2006
"Walter Bright" <newshound digitalmars.com> wrote:I think static closures are very useful and are a logical step for D (since they add power without changing syntax at all), just not now.Great! I'll look forward to their inclusion, in 2.0 or whenever it's ready. Søren J. Løvborg web kwi.dk
Feb 04 2006
Walter Bright wrote:I think static closures are very useful and are a logical step for D (since they add power without changing syntax at all), just not now.Woah! Wait a minute, what's the difference between static closures and dynamic closures? I think I got my terms mixed up in my post to this thread. Apparently D supports dynamic closures already (stack closures?). Are static closures what is being referred to as "true" closures then? -JJR
Feb 04 2006
"John Reimer" <terminal.node gmail.com> wrote in message news:ds2pr8$2cb1$1 digitaldaemon.com...Walter Bright wrote:That's my understanding.I think static closures are very useful and are a logical step for D (since they add power without changing syntax at all), just not now.Woah! Wait a minute, what's the difference between static closures and dynamic closures? I think I got my terms mixed up in my post to this thread. Apparently D supports dynamic closures already (stack closures?). Are static closures what is being referred to as "true" closures then?
Feb 08 2006
Walter Bright wrote:"John Reimer" <terminal.node gmail.com> wrote in message news:ds2pr8$2cb1$1 digitaldaemon.com...Sounds like a very bad name term then. "True" closures imply allocating the function frame/context on the heap, which is all but static. -- Bruno Medeiros - CS/E student "Certain aspects of D are a pathway to many abilities some consider to be... unnatural."Walter Bright wrote:That's my understanding.I think static closures are very useful and are a logical step for D (since they add power without changing syntax at all), just not now.Woah! Wait a minute, what's the difference between static closures and dynamic closures? I think I got my terms mixed up in my post to this thread. Apparently D supports dynamic closures already (stack closures?). Are static closures what is being referred to as "true" closures then?
Feb 09 2006
In article <drod3j$1jm0$1 digitaldaemon.com>, S�ren J. L�vborg says...I'm aware this has been discussed before, but I'd like to bring it up again (sorry!): D really should have true closures, or "stack delegates that can be returned" if you will. First of all, they're a very useful feature, allowing short and concise code.One option to work around this might be to add new syntax, say "new delegate" for this new behaviour, if implemented. Any comments? Am I the only one who'd find this useful? Is there already similar functionality, and I've just not found it? (If so, please elaborate!) S�ren J. L�vborg web kwi.dkIt is very useful as I see it. I recently looked at D at found it very promising (being disappointed in C++, after all these years). Currently, I use Python in my projects, and these "true" closures are quite natural in Python: def make_adder(n): def _adder(m): return n + m return _adder adder = make_adder(3) print adder(2) gives 5. (Sorry for this alien language snippet). Looking for something faster than Python, I tried to do that in D, but no way: import std.stdio; int delegate(int) make_adder(int what) { int _adder(int num) { return what + num; } return _adder; } void main() { int delegate(int) adder; adder = make_adder(3); writefln("%d", adder(2)); // Oops! the stack frame is gone :( } And worse, this doesn't work too: int delegate(int) make_adder(int what) { int *pw = new int; *pw = what; int _adder(int num) { return *pw + num; // Doesn't work either - the *pw is corrupted // after exiting the stack frame } return _adder; } About syntax, it would be convenient it I had another storage modifier, like "managed": int delegate(int) make_adder(managed int what) { int _adder(int num) { return what + num; // "What" is allocated on the heap // and managed with GC } return _adder; } Just my $0.02, and sorry for bad English. -- Eugene
Feb 10 2006









"Søren J. Løvborg" <web kwi.dk> 