[fpc-devel] Delphi anonymous methods

Alexander Klenin klenin at gmail.com
Mon Mar 4 06:57:38 CET 2013


On Mon, Mar 4, 2013 at 2:38 PM, Martin <lazarus at mfriebe.de> wrote:

> Closures, do not need to be written/declared in the middle of other code
> (in-line)

Strictly speaking, they do not. However, if the closures are required
to be named,
their expressive power and readability will suffer greatly.


Nevertheless, I agree what the implementation plan can be detailed even further:
1) Anonymous procedures *without* closures -- basically, just another
syntax for nested procedures.
  This is an important step for several reasons:
1.1) Syntax part is already done in a branch -- so just code clean-up
and merge left.
1.2) This would be useful as an optimization even after the full
closure support is implemented.
  Note that closures introduce significant overhead -- in particular,
if I understand correctly,
  in Delphi every local variable access becomes a reference to the
frame object field,
  *even in the parent procedure*. So "nested" anonymous procedures,
together with
  good inlining support, are crucial for the efficient implementation
of, for example,
  various "ForEach" methods.
  Of course, after closure support is implemented, closures should be
somehow distinguished from
  nested procedures -- either by additional syntax or via statical
code analysis by the optimizer.

2) Named closures support -- while I do think that anonymous
procedures are useful,
  Martin has a point too -- it feels strange that you *must* use
anonymous procedure in order to create a closure.
  Perhaps a named nested procedures should be explicitly extendabe to
closures too:

  type
    TIntProcRef = reference to procedure (X: Integer);
  function Outer(A, B: Integer): TIntProcRef;
  var
    i, j: Integer;
    procedure Inner1(C: Integer); A := i; end;
    procedure Inner2(D: Integer) closure (var A; i); A := i; end;
  begin
    Result := Inner1; // type error, Inner1 if of type "procedure ... is nested
    Result := Inner2; // ok
  end;
  Note the following details:
  2.0) The exact syntax is, of course, up to debate.
  2.1) Explicit closures allow specify by-value vs by-reference method
of closing over every variable,
  2.2) It is possible to even add modifiers like "const" and "out" in
the future.
  2.3) There should probably be some way to automatically close over
all variables used --
    for example:
    procedure Inner2(D: Integer) closure (var *); A := i; end; // All
variables by reference
    procedure Inner2(D: Integer) closure (*); A := i; end; // All
variables by value
    procedure Inner2(D: Integer) closure; A := i; end; // By reference
in Delphi mode, by value otherwise ??
  again, syntax is debatable
  2.4) Conversely, compiler could check that every variable reference
inside the closure is specified in the header,
    thus avoiding some typical errors.

3) Anonymous closures support -- a combination of the above:
   // Delphi-compatible syntax. Could be made to *not* use closures in
ObjFPC as an optimization.
   // Of course no-closures variant will fail if Apply procedure
stores the reference in a global variable for some reason.
   Apply(AIntArray, function (var AValue: Integer): Integer; begin
Result := a + x; end;)
   // Same as above, but a closure is forced.
  Apply(AIntArray, function (var AValue: Integer): Integer closure(x);
begin Result := a + x; end;)
   // Using "lambda" syntax.
   Apply(AIntArray, lambda TIntIntFunc closure(x) as a + x;)
   // Shortest variant.
   Apply(AIntArray, lambda TIntIntFunc as a + x;)

4) As stated above, either a syntax to explicitly turn off a closure
for anonymous procedure,
  or intelligent enough optimizer to do that automatically. I have
some ideas on both fronts,
  but I suggest we first discuss previous points.

--
Alexander S. Klenin



More information about the fpc-devel mailing list