Introduction
Getting Started
- QuickStart
Patterns
- Languages
- Supported Languages
- Python
- Java
- JavaScript
- TypeScript
- Node.js
- React
- Fastify
- Next.js
- Terraform
- C#
- C++
- C
- Go
- Rust
- Swift
- React Native
- Spring Boot
- Kotlin
- Flutter
- Ruby
- PHP
- Scala
- Perl
- R
- Dart
- Elixir
- Erlang
- Haskell
- Lua
- Julia
- Clojure
- Groovy
- Fortran
- COBOL
- Pascal
- Assembly
- Bash
- PowerShell
- SQL
- PL/SQL
- T-SQL
- MATLAB
- Objective-C
- VBA
- ABAP
- Apex
- Apache Camel
- Crystal
- D
- Delphi
- Elm
- F#
- Hack
- Lisp
- OCaml
- Prolog
- Racket
- Scheme
- Solidity
- Verilog
- VHDL
- Zig
- MongoDB
- ClickHouse
- MySQL
- GraphQL
- Redis
- Cassandra
- Elasticsearch
- Security
- Performance
Integrations
- Code Repositories
- Team Messengers
- Ticketing
Enterprise
Pascal is a procedural programming language designed in 1968-1969 and published in 1970 by Niklaus Wirth as a small, efficient language intended to encourage good programming practices using structured programming and data structuring.
Pascal, despite being designed to encourage good programming practices, has several common anti-patterns that can lead to maintainability problems and bugs. Here are the most important anti-patterns to avoid when writing Pascal code.
(* Anti-pattern: Using GOTO statements *)
procedure ProcessData;
var
i: Integer;
begin
i := 1;
StartLoop:
if i > 100 then
goto EndLoop;
WriteLn('Processing item ', i);
i := i + 1;
goto StartLoop;
EndLoop:
WriteLn('Processing complete');
end;
(* Better approach: Use structured control flow *)
procedure ProcessData;
var
i: Integer;
begin
for i := 1 to 100 do
begin
WriteLn('Processing item ', i);
end;
WriteLn('Processing complete');
end;
Avoid using GOTO
statements, which make code difficult to understand and maintain. Use structured control flow constructs like for
, while
, repeat-until
, and if-then-else
instead.
(* Anti-pattern: Weak typing with variant records *)
type
DataKind = (IntKind, RealKind, StringKind);
DataRec = record
case Kind: DataKind of
IntKind: (IntValue: Integer);
RealKind: (RealValue: Real);
StringKind: (StringValue: String);
end;
procedure ProcessData(var Data: DataRec);
begin
(* Unsafe type handling *)
case Data.Kind of
IntKind: WriteLn('Integer: ', Data.IntValue);
RealKind: WriteLn('Real: ', Data.RealValue);
StringKind: WriteLn('String: ', Data.StringValue);
end;
end;
(* Better approach: Use object-oriented programming or generics *)
(* In modern Object Pascal (e.g., Free Pascal, Delphi) *)
type
IData = interface
procedure Display;
end;
TIntegerData = class(TInterfacedObject, IData)
private
FValue: Integer;
public
constructor Create(AValue: Integer);
procedure Display;
end;
TRealData = class(TInterfacedObject, IData)
private
FValue: Real;
public
constructor Create(AValue: Real);
procedure Display;
end;
TStringData = class(TInterfacedObject, IData)
private
FValue: String;
public
constructor Create(AValue: String);
procedure Display;
end;
procedure ProcessData(Data: IData);
begin
Data.Display; (* Polymorphic call *)
end;
Leverage Pascal’s strong typing system rather than using variant records or other techniques that circumvent type safety. In modern Object Pascal, use interfaces, classes, or generics for type-safe polymorphism.
(* Anti-pattern: Using global variables *)
var
CurrentUser: String;
LoggedIn: Boolean;
ErrorCount: Integer;
procedure Login(Username, Password: String);
begin
if ValidateCredentials(Username, Password) then
begin
CurrentUser := Username;
LoggedIn := True;
ErrorCount := 0;
end
else
begin
LoggedIn := False;
Inc(ErrorCount);
end;
end;
procedure ProcessUserData;
begin
if not LoggedIn then
Exit;
(* Process data for CurrentUser *)
end;
(* Better approach: Use parameter passing and return values *)
type
TUserSession = record
Username: String;
LoggedIn: Boolean;
ErrorCount: Integer;
end;
function Login(Username, Password: String): TUserSession;
var
Session: TUserSession;
begin
Session.Username := '';
Session.LoggedIn := False;
Session.ErrorCount := 0;
if ValidateCredentials(Username, Password) then
begin
Session.Username := Username;
Session.LoggedIn := True;
end
else
begin
Inc(Session.ErrorCount);
end;
Result := Session;
end;
procedure ProcessUserData(const Session: TUserSession);
begin
if not Session.LoggedIn then
Exit;
(* Process data for Session.Username *)
end;
Avoid using global variables, which create hidden dependencies and make code harder to understand, test, and maintain. Pass data explicitly through parameters and return values.
(* Anti-pattern: Poor error handling *)
procedure ReadDataFromFile(FileName: String);
var
F: TextFile;
Line: String;
begin
AssignFile(F, FileName);
Reset(F); (* May raise an exception if file doesn't exist *)
while not Eof(F) do
begin
ReadLn(F, Line);
ProcessLine(Line);
end;
CloseFile(F);
end;
(* Better approach: Proper error handling *)
procedure ReadDataFromFile(FileName: String);
var
F: TextFile;
Line: String;
begin
AssignFile(F, FileName);
try
Reset(F);
while not Eof(F) do
begin
ReadLn(F, Line);
ProcessLine(Line);
end;
except
on E: EInOutError do
WriteLn('File error: ', E.Message);
on E: Exception do
WriteLn('Error: ', E.Message);
finally
CloseFile(F);
end;
end;
Use proper error handling with try-except-finally
blocks (in modern Pascal) to handle exceptions and ensure resources are properly cleaned up, even when errors occur.
(* Anti-pattern: Using magic numbers *)
procedure CalculateArea(Radius: Real);
var
Area: Real;
begin
Area := 3.14159 * Radius * Radius;
WriteLn('Area: ', Area:0:2);
end;
procedure ResizeArray(var Arr: array of Integer);
var
NewArr: array of Integer;
i: Integer;
begin
SetLength(NewArr, Length(Arr) * 2);
for i := 0 to Length(Arr) - 1 do
NewArr[i] := Arr[i];
Arr := NewArr;
end;
(* Better approach: Use named constants *)
const
PI = 3.14159;
ARRAY_GROWTH_FACTOR = 2;
procedure CalculateArea(Radius: Real);
var
Area: Real;
begin
Area := PI * Radius * Radius;
WriteLn('Area: ', Area:0:2);
end;
procedure ResizeArray(var Arr: array of Integer);
var
NewArr: array of Integer;
i: Integer;
begin
SetLength(NewArr, Length(Arr) * ARRAY_GROWTH_FACTOR);
for i := 0 to Length(Arr) - 1 do
NewArr[i] := Arr[i];
Arr := NewArr;
end;
Avoid using magic numbers (hardcoded numeric literals) in your code. Use named constants to make your code more readable and maintainable.
(* Anti-pattern: Poor resource management *)
procedure ProcessFile(FileName: String);
var
F: TextFile;
begin
AssignFile(F, FileName);
Reset(F);
(* Process file... *)
(* What if an exception occurs before this point? *)
CloseFile(F);
end;
(* Better approach: Proper resource management *)
procedure ProcessFile(FileName: String);
var
F: TextFile;
begin
AssignFile(F, FileName);
try
Reset(F);
(* Process file... *)
finally
CloseFile(F);
end;
end;
Always ensure that resources (files, memory, etc.) are properly released, even when exceptions occur. Use try-finally
blocks to guarantee cleanup code is executed.
(* Anti-pattern: Using arrays for dynamic collections *)
procedure ProcessItems;
var
Items: array[1..100] of String; (* Fixed size *)
Count: Integer;
i: Integer;
begin
Count := 0;
(* Add items *)
while not EndOfInput do
begin
Inc(Count);
if Count > 100 then
begin
WriteLn('Too many items!');
Exit;
end;
Items[Count] := ReadItem();
end;
(* Process items *)
for i := 1 to Count do
ProcessItem(Items[i]);
end;
(* Better approach: Use dynamic data structures *)
procedure ProcessItems;
var
Items: TStringList; (* Dynamic size *)
i: Integer;
begin
Items := TStringList.Create;
try
(* Add items *)
while not EndOfInput do
Items.Add(ReadItem());
(* Process items *)
for i := 0 to Items.Count - 1 do
ProcessItem(Items[i]);
finally
Items.Free;
end;
end;
Use appropriate data structures for your needs. In modern Pascal, use dynamic arrays, lists, dictionaries, and other collection classes instead of fixed-size arrays.
(* Anti-pattern: Everything in one file *)
program MonolithicApp;
(* Hundreds of types, variables, and procedures all in one file *)
begin
(* Main program code *)
end.
(* Better approach: Use units for modular code organization *)
unit UserManagement;
interface
type
TUser = record
Username: String;
Email: String;
end;
function CreateUser(const Username, Email: String): TUser;
procedure SaveUser(const User: TUser);
function FindUser(const Username: String): TUser;
implementation
(* Implementation of user management functions *)
end.
unit FileHandling;
interface
procedure SaveToFile(const FileName, Content: String);
function LoadFromFile(const FileName: String): String;
implementation
(* Implementation of file handling functions *)
end.
program ModularApp;
uses
UserManagement, FileHandling;
begin
(* Main program code using the units *)
end.
Organize your code into units (modules) with clear responsibilities. This improves maintainability, reusability, and compilation times.
(* Anti-pattern: Weak type definitions *)
procedure ProcessPerson(Name: String; Age: Integer; Height: Real);
begin
(* What if parameters are passed in the wrong order? *)
WriteLn('Name: ', Name, ', Age: ', Age, ', Height: ', Height:0:2);
end;
(* Usage *)
ProcessPerson('John', 25, 1.75); (* Correct *)
ProcessPerson(25, 'John', 1.75); (* Compiler won't catch this error! *)
(* Better approach: Use strong type definitions *)
type
TPersonName = String;
TAge = Integer;
THeight = Real;
TPerson = record
Name: TPersonName;
Age: TAge;
Height: THeight;
end;
procedure ProcessPerson(const Person: TPerson);
begin
WriteLn('Name: ', Person.Name, ', Age: ', Person.Age, ', Height: ', Person.Height:0:2);
end;
(* Usage *)
var
Person: TPerson;
begin
Person.Name := 'John';
Person.Age := 25;
Person.Height := 1.75;
ProcessPerson(Person);
end;
Use strong type definitions and structured data types to make your code more type-safe and self-documenting.
(* Anti-pattern: Using var parameters instead of function results *)
procedure CalculateSum(A, B: Integer; var Result: Integer);
begin
Result := A + B;
end;
(* Usage *)
var
X, Y, Sum: Integer;
begin
X := 5;
Y := 10;
CalculateSum(X, Y, Sum);
WriteLn('Sum: ', Sum);
end;
(* Better approach: Use function results *)
function CalculateSum(A, B: Integer): Integer;
begin
Result := A + B;
end;
(* Usage *)
var
X, Y: Integer;
begin
X := 5;
Y := 10;
WriteLn('Sum: ', CalculateSum(X, Y));
end;
Use function return values instead of var
parameters for outputs. This makes the code more readable and allows for function composition.
(* Anti-pattern: Using fixed-length strings and manual concatenation *)
type
TName = array[1..50] of Char;
procedure FormatName(var FullName: TName; FirstName, LastName: TName);
var
i, j: Integer;
begin
(* Clear the destination *)
for i := 1 to 50 do
FullName[i] := ' ';
(* Copy first name *)
i := 1;
while (i <= 50) and (FirstName[i] <> ' ') do
begin
FullName[i] := FirstName[i];
Inc(i);
end;
(* Add space *)
FullName[i] := ' ';
Inc(i);
(* Copy last name *)
j := 1;
while (i <= 50) and (j <= 50) and (LastName[j] <> ' ') do
begin
FullName[i] := LastName[j];
Inc(i);
Inc(j);
end;
end;
(* Better approach: Use dynamic strings *)
function FormatName(const FirstName, LastName: String): String;
begin
Result := FirstName + ' ' + LastName;
end;
Use modern string types and operations instead of fixed-length character arrays and manual string manipulation. This makes your code more readable and less error-prone.
(* Anti-pattern: Poor or no documentation *)
function CalculateValue(A, B, C: Integer): Integer;
begin
Result := A * B + C;
end;
(* Better approach: Proper documentation *)
(*
* Calculates a weighted sum of two values plus an offset.
*
* @param A The first value to be multiplied.
* @param B The second value to be multiplied.
* @param C The offset to be added after multiplication.
* @return The result of A * B + C.
*)
function CalculateValue(A, B, C: Integer): Integer;
begin
Result := A * B + C;
end;
Document your code with comments that explain the purpose, parameters, return values, and any important details or assumptions. This makes your code more maintainable and easier for others to understand.
(* Anti-pattern: Procedural code with poor encapsulation *)
type
TRectangle = record
X, Y, Width, Height: Integer;
end;
procedure DrawRectangle(const R: TRectangle);
begin
(* Drawing code *)
end;
procedure MoveRectangle(var R: TRectangle; DX, DY: Integer);
begin
R.X := R.X + DX;
R.Y := R.Y + DY;
end;
procedure ResizeRectangle(var R: TRectangle; NewWidth, NewHeight: Integer);
begin
R.Width := NewWidth;
R.Height := NewHeight;
end;
(* Better approach: Object-oriented design *)
type
TRectangle = class
private
FX, FY, FWidth, FHeight: Integer;
public
constructor Create(X, Y, Width, Height: Integer);
procedure Draw;
procedure Move(DX, DY: Integer);
procedure Resize(NewWidth, NewHeight: Integer);
property X: Integer read FX;
property Y: Integer read FY;
property Width: Integer read FWidth;
property Height: Integer read FHeight;
end;
constructor TRectangle.Create(X, Y, Width, Height: Integer);
begin
FX := X;
FY := Y;
FWidth := Width;
FHeight := Height;
end;
procedure TRectangle.Draw;
begin
(* Drawing code *)
end;
procedure TRectangle.Move(DX, DY: Integer);
begin
FX := FX + DX;
FY := FY + DY;
end;
procedure TRectangle.Resize(NewWidth, NewHeight: Integer);
begin
FWidth := NewWidth;
FHeight := NewHeight;
end;
In modern Object Pascal, use object-oriented design principles like encapsulation, inheritance, and polymorphism to create more maintainable and extensible code.