[fpc-devel] Delphi anonymous methods

Sven Barth pascaldragon at googlemail.com
Sun Mar 3 11:09:22 CET 2013


On 03.03.2013 08:07, Vasiliy Kevroletin wrote:
>> If the "x" is really located in the FrameObject and that object exists
>> only once then it is rather likely that the output of 'Before 123: '
>> is different from the output of 'After 123: ', right? Again I think
>> this is a bad thing...
> I was wrong. Local variables of anonymous function located where they
> should be: on stack.

That's a relieve :)

>>  So in my opinion an implementation with a stricter division between
>> the single instances of an anonymous function would be the better choice
> Am I understand correctly that loop
>
> for i := 1 to 5 do
>    procArr[i] := procedure begin
>                           DoSmth();
>                         end;
>
> should create 5 separate instances?

Not the code should be seperate, but where the captured data is located. 
In Delphi there will only be one FrameObject shared by all anonymous 
functions, but in my opinion it would be better if we have each a 
"Storage Object" for each "instance" of an anonymous function. So in the 
above example there would indeed be 5 such instance (but the code would 
only exist once).

> 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.

Little side note: You can create static variables in Pascal, it's just 
not as you'd expect them ;)

=== example begin ===

procedure Test;
const
   FooBar: Integer = 42;
begin
   Writeln(FooBar);
{$push}
{$J+}
   FooBar := 21;
{$pop}
end;

Test; // 42
Test; // 21

=== example end ===

This works since at least Turbo Pascal times (and the $J enables 
writeable typed constants) and was used as the "poor man's visibility 
handling"

But I don't understand what you continue to write: if we create a static 
variable it would be shared by all instances and not be specific to each 
instance.
Also in my proposal the instances would not be equivalent. Delphi seems 
to create these anonymous functions

=== code begin ===

procedure Something;
var
   x, y: Integer;
   proc: reference to procedure;
   func: reference to function(aX: Integer): Integer;
begin
   x := 42;
   y := 21;
   proc := procedure
           begin
             Writeln(x);
           end;
   func := function(aX: Integer): Integer
           begin
             Result := aX * y;
           end;
end;

=== code end ===

this way (note: not necessarily compileable code):

=== code begin ===

procedure Something;

   type
     FrameObjectClass = class(TInterfacedObject)
       x, y: Integer;
       procedure CallProc1;
       function CallFunc1(aX: Integer): Integer;
     end;

     procedure FrameObjectClass.CallProc1;
     begin
       Writeln(x);
     end;

     function FrameObjectClass.CallFunc1(aX: Integer): Integer;
     begin
       Result := aX * y;
     end;

   var
     FrameObject: FrameObjectClass;
     proc: reference to procedure;
     func: reference to function(aX: Integer): Integer;
begin
   FrameObject := FrameObjectClass.Create;
   FrameObject.x := 42;
   FrameObject.y := 21;
   proc := @FrameObject.CallProc1;
   func := @FrameObject.CallFunc1;
end;

=== code end ===

Now I would like to see it done like this:

=== code begin ===

procedure Something;

type
   StorageProc1 = class(TInterfacedObject)
     x: Integer;
     procedure Invoke;
   end;

   StorageFunc1 = class(TInterfacedObject)
     y: Integer;
     function Invoke(aX: Integer): Integer;
   end;

var
   x, y: Integer;
   tmp: TInterfacedObject;
   proc: reference to procedure;
   func: reference to function(aX: Integer): Integer;
begin
   x := 42;
   y := 21;
   tmp := StorageProc1.Create;
   tmp.x := x;
   proc := @tmp.Invoke;
   tmp := STorageProc1.Create;
   tmp.y := y;
   func := @tmp.Invoke;
end;

=== code end ===

Note: I'm aware that one shouldn't use interfaces this way; this is only 
an example that should illustrate my point.

So for each assignment to a "reference to" procvar a new such Storage 
Object will be created and so a "arr[1] = arr[2]" would be false in case 
of the loop.

> Capturing variables by value will allow simple creation of parametrised
> closures. Now you also can do parametrised anonymous method but you
> should wrap generation of such method into other anonymous function. For
> example like this:
>
> === example
>
> for i := 1 to 5 do
>    procArr[i] := Call(
>      function: TProc
>      var stored: Integer;
>      begin
>        stored := i;
>        Result := procedure
>                  begin
>                    Writeln(stored);
>                  end;
>      end );
>
> ===
>
> This can be simplified by support of capturing by value (please don't
> make many attention to syntax, it's not main part of my proposal).
>
> === example
>
> for i := 1 to 5 do
>    procArr[i] := procedure
>                         capture i: byValue;
>                         begin
>                           Writeln(i);
>                        end;
>      end );
>
> ===
>
> What do you think about extension which will allow to specify how to
> capture. Either by value or by reference?

Maybe specifying how a variable should be captured would be a solution 
that can satisfy both worlds...

Regards,
Sven





More information about the fpc-devel mailing list