Lesson 14 · 14 of 20
Procedural types and callbacks · Pascal
Pass functions and methods as parameters in Object Pascal with procedural types and of object, the mechanism behind Lazarus events.

A procedural type describes the shape of a function: TPriceRule = function(price: Double): Double. Any function with that signature fits, and Sale (20 % off) and Clearance (50 % off) both do. In objfpc mode, `@Sale` takes a reference to the function so you can pass it around like any other value.
Checkout receives the pricing rule and calls it as if it were an ordinary function: rule(price). The same checkout routine works for any discount, present or future, without touching its code; that is what a callback is.
`of object` changes the game: TSaleEvent does not point to a free-standing function but to a method of a specific object. Internally it stores two things, the code and the object (Self). That is why @till.ShowSale carries till along and the method can print its Name, Till 1.
Before calling an event, check `Assigned(onSale)`: if nobody has subscribed it is nil and calling it would crash. This is how Lazarus events work: OnClick is a procedure(Sender: TObject) of object, and in DMarlon Studio's desktop apps every button hooks up a form method in exactly this way.
Code · main.pas
program main;type TPriceRule = function(price: Double): Double; // any function of this shape TSaleEvent = procedure(price, total: Double) of object; // a method + its object TTill = class Name: String; procedure ShowSale(price, total: Double); end;procedure TTill.ShowSale(price, total: Double);begin WriteLn('[', Name, '] ', price:0:2, ' -> ', total:0:2);end;function Sale(price: Double): Double;begin Result := price * 0.80;end;function Clearance(price: Double): Double;begin Result := price * 0.50;end;procedure Checkout(price: Double; rule: TPriceRule; onSale: TSaleEvent);begin if Assigned(onSale) then onSale(price, rule(price)); // only if someone listensend;var till: TTill;begin till := TTill.Create; till.Name := 'Till 1'; Checkout(59.90, @Sale, @till.ShowSale); Checkout(59.90, @Clearance, @till.ShowSale); till.Free;end.Output · fpc main.pas && ./main
[Till 1] 59.90 -> 47.92 [Till 1] 59.90 -> 29.95
Key points
- A procedural type is a function signature.
@Functiontakes the reference inobjfpcmode.of objectstores a method together with its object.- Check
Assignedbefore calling an event. - Lazarus events (
OnClickand friends) areof objectmethods.
Exercise
Write a Member rule that takes 5 EUR off prices above 30, create a second TTill named 'Till 2' and use it to charge 59.90 with @Member.