[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