Procedures & Functions

A procedure performs an action and returns no value:

11.1 Procedures

A procedure performs an action and returns no value:

procedure PrintLine(Width: Integer);
var
  I: Integer;
begin
  for I := 1 to Width do
    Write('-');
  Writeln;
end;

begin
  PrintLine(20);
end.

11.2 Functions and Result

A function returns a value. Assign it either to the implicit variable Result (FPC objfpc/delphi modes, Delphi) or to the function name (classic Pascal):

Result behaves like a local variable: you can read it, modify it repeatedly, and pass it to other routines. For non-managed types it is not initialized — always assign it on every path. For managed types (strings, dynamic arrays, interfaces), do not assume it starts empty either: initialize it explicitly.

function Square(X: Double): Double;
begin
  Result := X * X;        // modern style
end;

function Cube(X: Double): Double;
begin
  Cube := X * X * X;      // classic style; still valid
end;

11.3 Calling routines

PrintLine(10);
Y := Square(3.0) + Cube(2.0);
Square(5.0);            // allowed: result discarded ({$X+} extended syntax, the default)
Randomize;              // parentheses are optional for parameterless calls

11.4 Exit with a value

function Sign(X: Integer): Integer;
begin
  if X > 0 then Exit(1);
  if X < 0 then Exit(-1);
  Result := 0;
end;

11.5 Overloading

Several routines can share a name if their parameter lists differ. Delphi requires the overload directive; FPC requires it only in delphi mode, but writing it is good practice everywhere:

The return type alone cannot distinguish overloads. Ambiguous calls (e.g. passing a Byte where both Integer and Int64 overloads fit) are resolved by the "closest match" rules or rejected.

function Max(A, B: Integer): Integer; overload;
begin
  if A > B then Result := A else Result := B;
end;

function Max(A, B: Double): Double; overload;
begin
  if A > B then Result := A else Result := B;
end;

function Max(const A, B: string): string; overload;
begin
  if A > B then Result := A else Result := B;
end;

11.6 Default parameters

Default parameters must come last and must be constant expressions. You cannot skip a middle parameter.

procedure Greet(const Name: string; const Greeting: string = 'Hello';
                Excited: Boolean = False);
begin
  Write(Greeting, ', ', Name);
  if Excited then Writeln('!') else Writeln('.');
end;

begin
  Greet('Ann');                 // Hello, Ann.
  Greet('Bob', 'Hi');           // Hi, Bob.
  Greet('Cy', 'Hey', True);     // Hey, Cy!
end.

11.7 Inline routines

inline is a hint; the compiler may decline (recursive routines, routines with asm, routines whose body is not visible). FPC needs {$inline on} in some modes (on by default in objfpc/delphi since 3.x).

function Clamp(X, Lo, Hi: Integer): Integer; inline;
begin
  if X < Lo then Result := Lo
  else if X > Hi then Result := Hi
  else Result := X;
end;

11.8 Calling conventions

default in Delphi/FPC on x86: parameters in registers left-to-right on the stack (legacy)

C convention: right-to-left, caller cleans the stack Windows API convention COM: converts exceptions to HRESULT

stdcall on Windows, cdecl elsewhere (Delphi/FPC) On x86-64 and ARM there is one platform ABI; these directives matter mostly on 32-bit x86 and for documentation.