Closures

Top  Previous  Next

Closures are supported in Pas2JS with the same syntax as in Delphi. 

 

 myObject.MyEvent  :=

   procedure  (Sender:  TObject);

   begin

    //

   end;

 

You are allowed to use a named local procedure as a closure, with optional capture of local variables, allowing for neater layout of code. 

 

begin

   procedure  MyLocalProc(Sender:  TObject);

   begin

     //

   end;

 

 myObject.MyEvent  :=  MyLocalProc;

 

end;

 

 

The term closures is very frequently treated as a synonym for the term anonymous methods

Though closures are specific to anonymous methods, they are not the same concept. 

Anonymous methods are possible without closures. 

Do not use the terms interchangeably. Anonymous methods have some considerations for variable scope.  

 

In traditional Pascal, anything in a var block which exists physically above the code which references is visible, are called 

nested methods, for example:

 

 Pas2JS nested routine example.

 

 

Code example: Using nested methods

procedure TForm1.W3Button22Click(Sender: TObject);

   procedure OuterMethod;

    var

      upperScope: string;

 

        procedure InnerMethod;

        var innerScope: string;

        begin

          // Here, upperScope and innerScope are visible.

          // lowerScope is not visible.

          innerScope:= 'inner string';

          writeln(innerScope + ' ' + upperScope);

        end;

 

   var

    lowerScope: string;

   begin

     upperScope:= 'upper string';

     lowerScope := 'lower string';

      // upperScope and lowerScope are visible here.

     writeln(upperScope + ' ' + lowerScope );

     InnerMethod;

   end;

 

begin

   OuterMethod;

end;

upper string lower string

inner string upper string

-------------------------

 

The method InnerMethod only lives in one place and is not visible anywhere except in the OuterMethod body. 

The variable lowerScope is not visible in the InnerMethod, so we can not have direct access to this variable in the 

InnerMethod, this variable is not in the scope of the method InnerMethod. To accomplish this, we can use closures.

 

 

 

Code example: Using Closure with Pas2JS

type

  TProc = reference to procedure;

 

type

  TMyClass4 = class

  protected

 

  public

    procedure DoStuff;

    procedure DoStuff2;

    function AddDays(aDays: integer): Float;

    function AddDays2(aDays: integer): Float;

  end;

 

 

procedure TMyClass4.DoStuff;

var

  j: integer;

  ShowCounter: TProc; // Method no longer has a parameter.

begin

  ShowCounter :=

    procedure

    begin

      // j is known here because it is wrapped in a closure

      // and made available to us!

      //for j := 1 to 10 do  --> not allowed

      writeln(IntToStr(j));

    end;

 

  for j := 1 to 10 do

    ShowCounter; // j is no longer passed

end;

 

 

procedure TForm1.W3Button23Click(Sender: TObject);

var myObj : TMyClass4;

begin

  myObj := TMyClass4.Create;

 try

  myObj.DoStuff;

 finally

  myObj.Free;

 end;

 

end;

myObj.DoStuff;  //j is available because it is wrapped in a closure!

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

------------------------------------------------

 

Using closures, we can have direct access to counter from with the anonymous method without using a parameter.  

Pas2JS will place j in the scope of the method and make it available to us without any work on our part.

 

Note: you can not have a for loop which uses a counter which exists in the closure (the counter must be local).  

 

 

procedure TMyClass4.DoStuff2;

var

  ShowCounter: TProc;

begin

  ShowCounter :=

    procedure

    var j: integer;  // j is now local.

    begin

      for j := 1 to 10 do

        writeln(IntToStr(j));

    end;

 

  ShowCounter;

end;

myObj.DoStuff2;  //you can have for loop because the counter j is declared as local!

1, 2, 3, 4, 5, 6, 7, 8, 9, 10

----------------------

 

Result in Closures

 

 

The Result value can live in a closure in Pas2JS.  For example:

 

WriteLn( myObj.AddDays(10) );

function TMyClass4.AddDays(aDays: integer): Float;

var

  rc: TDateTime;

  p: TProc;

begin

  rc := now;

 

  p :=

    procedure

    begin

      // RESULT can live in a closure.

      result := rc + aDays;

    end;

 

  p;

end;

 

8/19/2014 19:09 = TODAY

8/29/2014 22:09 = 41880.92333288195 

-----------------------------------

 

 

WriteLn( myObj.AddDays2(10) );

function TMyClass4.AddDays2(aDays: integer): Float;

var

  rc: TDateTime;

  p: TProc;

begin

  rc := now;

 

  p :=

    procedure

    begin

      // rc can live in a closure.

      rc := rc + aDays;

    end;

 

  p;

  result := rc;

end;

8/19/2014 19:09 = TODAY

8/29/2014 22:09 = 41880.92333288195

-----------------------------------

Note that you can use a variable to reference the return value in outer scope, hence it requires closure. 

 

 

More details see Anonymous methods