[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