Value closures (no GC allocation)
MakersF via Digitalmars-d
digitalmars-d at puremagic.com
Mon May 29 14:26:11 PDT 2017
On Thursday, 25 May 2017 at 03:10:04 UTC, Adam D. Ruppe wrote:
> Snip
I think the discussion is going in two orthogonal directions,
which are:
1) Capture by value. This is the ability to have a lambda which
behaves as expected in the example Vittorio showed
> void delegate()[] arr;
>
> foreach(i; 0..5)
> {
> arr ~= () => writeln(i);
> }
>
> foreach(f; arr)
> {
> f();
> }
>
>This is going to print "4 4 4 4"
2) Allocation of the lambda context. Whether this is performed by
the GC, in the stack, or with another mechanism.
The reason these two points are orthogonal is that, as Adam
showed, the ability to achieve one of the point is independent
from the other (the capture by reference context allocation can
be changed with the scope keyword, and from my understanding it's
an implementation detail, it does not guarantee the use of the
GC. And the value of capture by value could be allocated on the
heap with the GC).
I see two weaknesses in this proposal:
1) The lambda is not compatible with a delegate anymore
2) There need to be a special notation for reference capture
Adam said
> I'm against that, no need adding it and it complicates the
> whole thing. For example, "ref int _i;" as a struct member;
> there's no such thing in D. (the compiler could do it but
> still). And you'd have to explain the lifetime. Just no point
> doing this, the current behavior is completely fine for this.
The other problem I see, which is easily noticeable when defining
the new grammar, is that we are proposing a grammar which is a
superset of the current one, but we are proposing it as an
addition.
One of the assumption of D is that the GC is not a problem in the
vast majority of the situations, and you want things to just work.
I think there is big value in the capture context not having to
be explicit (even if usually I much prefer explicity over
implicity).
Considered this, I'd propose an extension of the lambda syntax
instead of an addition of a ValueFunctionLitteral.
Given the syntax
> [...] (...) ... {...}
which is composed of what I will call "capture", "parameters",
"attributes", "body" (with capture and attributes optional)
the semantic would be:
- if no capture is provided the behavior is equivalent to today
(automatic capturing by reference in a context allocated however
the compiler prefers). This provides full backward compatibility.
Example
(int i) { writeln(i + j);}
j is captured by reference in a context allocated (possibly) on
the GC. This is compatible with a delegate void(int).
- if capture is provided, then the lambda is rewritten as a
struct, which the explicit variables in the capture list being
captured by value, and the remaining being captured by reference
(so there would still be implicit capture, which is considered
convenient in many programming languages).
This is potentially unsafe since a reference could be copied when
copying the struct, and the struct might outlive the referred
object.
Example
[x] (int i) { writeln(i + j + x);}
x is captured by value in a local struct.
j is captured by reference in a local struct.
This is not a delegate anymore (different ABI).
- if "delegate" is used, then the capture context is allocated
however the compile prefers
[x] delegate (int i) { writeln(i + j + x);}
x is captured by value in a (possibly) GC context.
j is captured by reference in a (possibly) GC context.
This is a delegate (compatible ABI).
This thus is an extension of the lambda syntax to allow capturing
by value, but maintaining all the current properties and
providing a way to remain compatible with the delegate functions.
You can also use the @nogc attribute to verify that the GC is not
used (and in case also "delegate" is used "scope" will need to be
used so that the compiler does not require the GC).
What are your opinions?
Please state if there are some misconceptions on the current
situation.
More information about the Digitalmars-d
mailing list