[fpc-devel] Delphi anonymous methods
Vasiliy Kevroletin
kevroletin at gmail.com
Tue Mar 5 15:18:00 CET 2013
On 03/05/2013 05:34 AM, waldo kitty wrote:
>> It is as I thought about closures before. But this is useless without
>> capturing
>> of variables by value. During creation of anonymous method you *can
>> not bind any
>> values* to it. Anonymous method have only references to captured
>> variables.
>> Pascal don't allows to create static variables inside function like
>> in c-like
>> languages. So you also not able to create unique static variable
>> which available
>> only to one instance of same anonymous method. Even if implementation
>> will
>> create many separate objects for one anonymous function then all
>> instances will
>> be equivalent. Because all instances will refer to same data.
>
> i'm trying to understand what you mean by
>
> > Pascal don't allows to create static variables inside function like
> in c-like
> > languages.
>
> i've done something that i think is what you speak of but it was in
> Borland's Turbo Pascal... at least TP6... i don't remember how we did
> it though and can't find any sample code in my library :(
>
> what happened is that the value did not get cleared between
> invocations of the routine and subsequent executions could use the
> value already stored... this breaks, of course, if the memory area for
> the routine is cleared first...
>
> _______________________________________________
> fpc-devel maillist - fpc-devel at lists.freepascal.org
> http://lists.freepascal.org/mailman/listinfo/fpc-devel
Word "static" is mistake here, sorry. It should be "local" variable instead.
As it was said in this thread: closure is a way to create object (no
mean named closure or anonymous). Closure is a routine with all
variables which it accesses (or "captures" in terminology of Delphi or
C++). So closure is routine + captured data. I expected that Delphi will
create one object for one closure.
In example below I was expecting to see creation of 10 different
objects. But Delphi creates only 1 object. I was surprised.
=== example
for i := 1 to 10 begin
arr[i] := procedure begin
...
end;
===
All arr[i] have same value(same reference).
Actually Delphi creates object(called FrameObject in Delphi's docs) for
closure otherwise it will not work. But Delphi creates this object not
for every closure but only for procedure where closure defined. This
details described in pdf file which I attached in first message of
thread. Also Sven created good comments which describes how Delphi
implements closures and how capturing by value should work
http://lists.freepascal.org/lists/fpc-devel/2013-March/031588.html.
My confused comment can be reformulated: Delphi doesn't create object
for each closure because there is no need to do it. But why many other
languages should create object for each closure but Delphi need not ?
Because Pascal allows to declare local variables only in beginning of
routine. But many c-like languages allows to declare local variables in
middle of routine. I will describe on example.
Imagine that you want to create many similar objects in loop like this:
=== example
type TWriter = class
data: Integer;
constructor Create(i: Integer);
procedure Execute;
end;
constructor TWriter.Create(i: Integer); begin data := i; end;
procedure TWriter.Execute; begin Writeln(data); end;
var i: Integer;
arr: array[1..10] of TWriter;
begin
for i := 1 to 10 do
arr[i] := TWriter.Create(i);
for i := 1 to 10 do
arr[i].Execute;
end.
===
=== output
1 2 3 4 5 6 7 8 9 10
===
If someone will try to make same trick using Delphi's anonymous
methods(Delphi's implementation of closures) he may try to do this
=== wrong
for i := 1 to 10 do
procArr[i] := procedure begin
Writeln(i);
end;
for j := 1 to 10 do
procArr[j]();
===
=== output
11 11 11 11 11 11 11 11 11 11
===
Why? Because all procedures from procArr captured variable 'i' by
reference. They access same variable. So this is a problem to simply
create "private" data for closure. But you can do it like this:
=== right
function Factory(data: Integer): TProc;
begin
Result := procedure begin
Writeln(data);
end;
end;
...
for i := 1 to 10 do
procArr[i] := Factory(i);
for j := 1 to 10 do
procArr[j]();
===
=== output
1 2 3 4 5 6 7 8 9 10
===
Just as expected.
Now about local variables and why in other languages simple creation of
"private" variable for closure is not a problem.
=== perl
for (my $i = 0; $i < 10; ++$i) {
{
my $stored = $i;
$f[$i] = sub { printf $stored }
}
}
for (my $j = 0; $j < 10; ++$j) {
$f[$j]();
}
===
=== output
0123456789
===
This is works because:
- each loop iteration creates new variable $stored
- closure captures $stored
- variable $stored is accessible only in it's declaring block { .. }
; at end of loop iteration only closure will save link to it.
So Perl have to create new object for each closure because each closure
may contains private data. In Delphi closures also can have private
data, but closures created in same function will capture same data.
Closures created in same line of code will not only refer to same data
but also will have same behaviour. That is why in 1st example of my
email Delphi all elements of arr are equal.
Vasiliy K.
More information about the fpc-devel
mailing list