Skip to content
Lesson 6 of 7

Step 1 of 5 · Reading · ~2 min

Learn

Strings, Procedural Types, Variants

Pascal supports first-class function values via procedural types — types that name a procedure or function signature.

Everything below writes a function's return value as Result. That name only exists in Object Pascal dialects, so every program in this lesson (and the exercise) opens with {$mode objfpc}{$H+}; without it FPC 3.0.4 answers Identifier not found "Result".

Declaring

type
    TIntFunc = function(x: integer): integer;
    TIntProc = procedure(x: integer);
    TBinOp = function(a, b: integer): integer;

Using

function Square(x: integer): integer;
begin
    Result := x * x;
end;

function Add(a, b: integer): integer;
begin
    Result := a + b;
end;

var
    f: TIntFunc;
    op: TBinOp;
begin
    f := @Square;            { take address }
    WriteLn(f(7));            { 49 }

    op := @Add;
    WriteLn(op(3, 4));        { 7 }
end.

The @ is required to take the address of a function (FPC and Delphi both work this way).

Higher-order patterns

Map with a callback:

function MapInts(a: array of integer; f: TIntFunc): TIntArray;
var i: integer;
begin
    SetLength(Result, Length(a));
    for i := 0 to High(a) do
        Result[i] := f(a[i]);
end;

var
    nums, squared: TIntArray;
begin
    nums := [1, 2, 3, 4, 5];
    squared := MapInts(nums, @Square);
    { squared = [1, 4, 9, 16, 25] }
end.

Method pointers (procedure of object)

Point to a method on an instance — the variable carries both the function and its self:

type
    TButtonClick = procedure(Sender: TObject) of object;

    TForm = class
    public
        OnClick: TButtonClick;
    end;

The of object makes the type include a hidden Self pointer. This is how Delphi's event handlers work — every component property like OnClick is a procedure of object.

What Pascal does instead of a closure

A procedural-type variable stores an address and nothing else — it cannot capture a surrounding local. Delphi has anonymous methods (reference to function) that can; FPC only gained them in 3.3.x, and the 3.0.4 compiler behind this course rejects an inline function(x: integer): integer begin ... end outright with Illegal expression.

When a callback needs state, give the state a home and use a method pointer:

type
    TScaler = class
    public
        Multiplier: integer;
        function Apply(x: integer): integer;
    end;

    TIntMethod = function(x: integer): integer of object;

function TScaler.Apply(x: integer): integer;
begin
    Result := x * Multiplier;
end;

Multiplier is the captured variable, held explicitly on the object; of object is what lets one value carry both the code and the instance it belongs to.

Use cases

  • Sorting with custom comparators
  • Event-driven UI (OnClick, OnChange, OnKeyDown)
  • Strategy pattern: pass behavior as a value
  • Decoupling: caller provides callback, library doesn't depend on caller
  • Plugin systems

Most FPC code uses procedural types alongside classes — methods for state, plain procedures for stateless callbacks.

Up nextVariants and Type-SwitchingStrings, Procedural Types, Variants

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…