PONY λ M2 Modula-2

Pascal.CodeCompared.To/Modula-2

An interactive executable cheatsheet comparing Pascal and Modula-2

Free Pascal 3.2.2 ISO Modula-2 (GNU Modula-2 16.1)
Hello World & Structure
Hello, World
program Hello; begin writeln('Hello, World!'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; BEGIN WriteString("Hello, World!"); WriteLn; END Example.
Modula-2 replaces program with MODULE. There is no built-in writeln — text I/O is imported from STextIO. The module body closes with END ModuleName. (period, not semicolon).
Compiling
{ fpc hello.pas } { ./hello }
(* gm2 -fiso hello.mod -o hello *) (* ./hello *)
GNU Modula-2 (gm2) is the most widely available modern compiler. The -fiso flag selects ISO dialect, which this cheatsheet targets. Comments in Modula-2 use (* ... *) instead of Pascal's { ... }.
Printing integers
program Demo; begin writeln(42); writeln(-7); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt; BEGIN WriteInt(42, 0); WriteLn; WriteInt(-7, 0); WriteLn; END Example.
Modula-2 splits I/O across modules by type. SWholeIO handles whole-number I/O; the second argument to WriteInt is the minimum field width — 0 means no padding.
Printing real numbers
program Demo; begin writeln(3.14:8:2); writeln(2.718:1:4); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SRealIO IMPORT WriteReal; BEGIN WriteReal(3.14, 8); WriteLn; WriteReal(2.718, 8); WriteLn; END Example.
SRealIO.WriteReal takes the value and a total field width. Unlike Pascal's :fore:aft format notation, Modula-2 has no built-in decimal-place control — you get whatever the default float representation produces.
Variables & Types
Variable declarations
program Demo; var count : Integer; ratio : Real; letter: Char; flag : Boolean; begin count := 10; ratio := 3.14; letter := 'A'; flag := True; writeln(count, ' ', ratio:4:2); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn, WriteChar; FROM SWholeIO IMPORT WriteInt; FROM SRealIO IMPORT WriteReal; VAR count : INTEGER; ratio : REAL; letter: CHAR; flag : BOOLEAN; BEGIN count := 10; ratio := 3.14; letter := 'A'; flag := TRUE; WriteInt(count, 0); WriteLn; WriteReal(ratio, 6); WriteLn; WriteChar(letter); WriteLn; END Example.
Modula-2 uses all-uppercase reserved words: VAR, INTEGER, REAL, CHAR, BOOLEAN. The rest of the variable-declaration syntax is identical to Pascal.
Built-in types
program Demo; var n : Integer; { signed whole number } r : Real; { floating point } flag: Boolean; { True / False } ch : Char; { single character } { no unsigned integer type in standard Pascal } begin n := -42; writeln(n); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt, WriteCard; VAR signed : INTEGER; (* signed whole number *) unsigned: CARDINAL; (* unsigned: 0..MAX(CARDINAL) *) ratio : REAL; flag : BOOLEAN; letter : CHAR; BEGIN signed := -42; unsigned := 100; WriteInt(signed, 0); WriteLn; WriteCard(unsigned, 0); WriteLn; END Example.
CARDINAL is an unsigned whole-number type with no Pascal equivalent. Modula-2 strictly distinguishes signed (INTEGER) from unsigned (CARDINAL); mixing them requires an explicit conversion.
Constants
program Demo; const MaxSize = 100; Pi = 3.14159; Greeting = 'Hello'; begin writeln(MaxSize); writeln(Greeting); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; CONST MaxSize = 100; Pi = 3.14159; Greeting = "Hello"; BEGIN WriteInt(MaxSize, 0); WriteLn; WriteString(Greeting); WriteLn; END Example.
The CONST block syntax is identical to Pascal's except for the uppercase keyword. String literals use double quotes in Modula-2; single quotes are for CHAR literals.
Type declarations
program Demo; type Color = (Red, Green, Blue); Score = 0..100; IntArr = array[1..10] of Integer; var hue : Color; grade: Score; begin hue := Green; grade := 87; writeln(Ord(hue)); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt; TYPE Color = (Red, Green, Blue); Score = [0..100]; IntArr = ARRAY [1..10] OF INTEGER; VAR hue : Color; grade: Score; BEGIN hue := Green; grade := 87; WriteInt(ORD(hue), 0); WriteLn; WriteInt(grade, 0); WriteLn; END Example.
Modula-2 subrange types use square brackets: [0..100] instead of Pascal's bare 0..100. Enumeration types use the same parenthesized syntax. ORD is uppercase.
Enumeration types
program Demo; type Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); var today: Day; begin today := Wed; if today = Wed then writeln('Wednesday'); writeln(Ord(today)); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; TYPE Day = (Mon, Tue, Wed, Thu, Fri, Sat, Sun); VAR today: Day; BEGIN today := Wed; IF today = Wed THEN WriteString("Wednesday"); WriteLn; END; WriteInt(ORD(today), 0); WriteLn; END Example.
Enumeration types are syntactically identical in both languages. ORD returns the zero-based ordinal position of an enum value — ORD(Mon) = 0, ORD(Wed) = 2.
Strings
String representation
program Demo; var name : AnsiString; { dynamic length } shortstr: String[20]; { max 20 chars } begin name := 'Alice'; writeln(name, ' ', Length(name)); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; FROM Strings IMPORT Length; VAR name: ARRAY [0..29] OF CHAR; BEGIN name := "Alice"; WriteString(name); WriteLn; WriteInt(Length(name), 0); WriteLn; END Example.
Modula-2 has no built-in string type. All strings are ARRAY [0..N] OF CHAR, null-terminated. The Strings module provides operations. Direct assignment (name := "Alice") fills the array from the left and null-terminates it.
String length
program Demo; var name: AnsiString; begin name := 'Ada Lovelace'; writeln(Length(name)); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt; FROM Strings IMPORT Length; VAR name: ARRAY [0..29] OF CHAR; BEGIN name := "Ada Lovelace"; WriteInt(Length(name), 0); WriteLn; END Example.
The Strings module's Length function counts characters up to the first null byte, just like Pascal's Length for AnsiString.
String concatenation
program Demo; var first, last, full: AnsiString; begin first := 'Ada'; last := 'Lovelace'; full := first + ' ' + last; writeln(full); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM Strings IMPORT ConCat; VAR first : ARRAY [0..19] OF CHAR; last : ARRAY [0..19] OF CHAR; full : ARRAY [0..49] OF CHAR; BEGIN first := "Ada"; last := "Lovelace"; ConCat("Ada ", last, full); WriteString(full); WriteLn; END Example.
Strings.ConCat (note the capital C) takes two source strings and writes their concatenation into a destination array. There is no + operator for strings. Each destination array must be large enough to hold the result.
String comparison
program Demo; var name: AnsiString; begin name := 'Alice'; if name = 'Alice' then writeln('Equal'); if name > 'Adam' then writeln('After Adam'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM Strings IMPORT CompareStr; VAR name: ARRAY [0..19] OF CHAR; BEGIN name := "Alice"; IF CompareStr(name, "Alice") = 0 THEN WriteString("Equal"); WriteLn; END; IF CompareStr(name, "Adam") > 0 THEN WriteString("After Adam"); WriteLn; END; END Example.
Strings cannot be compared with = or > because they are arrays, not scalar values. Strings.CompareStr returns 0 for equal, a negative number if the first is less, and a positive number if greater.
Control Flow & Loops
If / elsif
program Demo; var score: Integer; begin score := 75; if score >= 90 then writeln('A') else if score >= 80 then writeln('B') else if score >= 70 then writeln('C') else writeln('F'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; VAR score: INTEGER; BEGIN score := 75; IF score >= 90 THEN WriteString("A"); WriteLn; ELSIF score >= 80 THEN WriteString("B"); WriteLn; ELSIF score >= 70 THEN WriteString("C"); WriteLn; ELSE WriteString("F"); WriteLn; END; END Example.
Modula-2 uses ELSIF (like Ada) and closes the whole if-chain with a single END;. There is no begin/end pair inside branches — the END terminates the entire structure.
Case statement
program Demo; var month: Integer; begin month := 6; case month of 12, 1, 2: writeln('Winter'); 3..5: writeln('Spring'); 6..8: writeln('Summer'); 9..11: writeln('Autumn'); end; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; VAR month: INTEGER; BEGIN month := 6; CASE month OF 12, 1, 2 : WriteString("Winter"); WriteLn; | 3..5 : WriteString("Spring"); WriteLn; | 6..8 : WriteString("Summer"); WriteLn; | 9..11 : WriteString("Autumn"); WriteLn; END; END Example.
Modula-2 CASE branches are separated by | instead of Pascal's semicolons, and the block closes with END instead of end;. The ELSE clause (default) uses the same keyword as Pascal.
For loop
program Demo; var i: Integer; begin for i := 1 to 5 do write(i, ' '); writeln; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; VAR i: INTEGER; BEGIN FOR i := 1 TO 5 DO WriteInt(i, 0); WriteString(" "); END; WriteLn; END Example.
The FOR loop syntax is nearly identical to Pascal's — just uppercase keywords and END instead of begin/end closing the body.
While loop
program Demo; var count: Integer; begin count := 1; while count <= 5 do begin write(count, ' '); Inc(count); end; writeln; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; VAR count: INTEGER; BEGIN count := 1; WHILE count <= 5 DO WriteInt(count, 0); WriteString(" "); INC(count); END; WriteLn; END Example.
Modula-2 replaces Pascal's begin/end pair inside while with just END. INC and DEC are built-in procedures (uppercase) that increment and decrement a variable.
Repeat-until
program Demo; var count: Integer; begin count := 1; repeat write(count, ' '); Inc(count); until count > 5; writeln; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; VAR count: INTEGER; BEGIN count := 1; REPEAT WriteInt(count, 0); WriteString(" "); INC(count); UNTIL count > 5; WriteLn; END Example.
The REPEAT...UNTIL loop is one of the most direct Pascal-to-Modula-2 translations — only the capitalisation changes. Both execute the body at least once and test the condition at the end.
Loop with mid-body exit
{ Free Pascal: use break } program Demo; var i: Integer; begin i := 0; while True do begin Inc(i); if i >= 3 then break; write(i, ' '); end; writeln; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; VAR i: INTEGER; BEGIN i := 0; LOOP INC(i); IF i >= 3 THEN EXIT; END; WriteInt(i, 0); WriteString(" "); END; WriteLn; END Example.
Modula-2's LOOP...END is an infinite loop; EXIT breaks out of it. This is the idiom for loops that need to exit from the middle — equivalent to Free Pascal's break. The EXIT may appear anywhere in the body.
Procedures & Functions
Procedures
program Demo; procedure Greet(name: String); begin writeln('Hello, ', name, '!'); end; begin Greet('Alice'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; PROCEDURE Greet(name: ARRAY OF CHAR); BEGIN WriteString("Hello, "); WriteString(name); WriteString("!"); WriteLn; END Greet; BEGIN Greet("Alice"); END Example.
Modula-2 procedures look almost identical to Pascal's: uppercase keywords, the procedure name repeated after END. The string parameter type is ARRAY OF CHAR (an open array), which accepts arrays of any size.
Functions (procedures with return type)
program Demo; function Square(n: Integer): Integer; begin result := n * n; end; begin writeln(Square(7)); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt; PROCEDURE Square(n: INTEGER): INTEGER; BEGIN RETURN n * n; END Square; BEGIN WriteInt(Square(7), 0); WriteLn; END Example.
Modula-2 has no separate function keyword. A function is just a PROCEDURE with a return type after the closing ). RETURN value sends back the result — Pascal's result := becomes an explicit RETURN statement.
VAR parameters
program Demo; procedure Swap(var a, b: Integer); var temp: Integer; begin temp := a; a := b; b := temp; end; var x, y: Integer; begin x := 5; y := 10; Swap(x, y); writeln(x, ' ', y); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; PROCEDURE Swap(VAR a, b: INTEGER); VAR temp: INTEGER; BEGIN temp := a; a := b; b := temp; END Swap; VAR x, y: INTEGER; BEGIN x := 5; y := 10; Swap(x, y); WriteInt(x, 0); WriteString(" "); WriteInt(y, 0); WriteLn; END Example.
VAR parameter syntax is identical in both languages — only the capitalisation differs. VAR passes by reference, letting the procedure modify the caller's variable.
Nested procedures
program Demo; procedure Outer; procedure Inner(n: Integer); begin writeln('Inner: ', n); end; begin Inner(42); end; begin Outer; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; PROCEDURE Outer; PROCEDURE Inner(n: INTEGER); BEGIN WriteString("Inner: "); WriteInt(n, 0); WriteLn; END Inner; BEGIN Inner(42); END Outer; BEGIN Outer; END Example.
Like Pascal, Modula-2 supports fully nested procedures with access to the outer procedure's local variables. The inner procedure is declared in the outer procedure's local declaration section (before BEGIN).
Modules
Module structure
{ Pascal unit: one file } unit Geometry; interface function Area(radius: Real): Real; implementation uses Math; function Area(radius: Real): Real; begin result := Pi * radius * radius; end; end.
(* geometry.def — definition module *) DEFINITION MODULE Geometry; PROCEDURE Area(radius: REAL): REAL; END Geometry. (* geometry.mod — implementation module *) IMPLEMENTATION MODULE Geometry; FROM MathLib0 IMPORT pi; PROCEDURE Area(radius: REAL): REAL; BEGIN RETURN pi * radius * radius; END Area; END Geometry.
Modula-2 splits every module into a definition file (public interface) and an implementation file (private details) — the compiler enforces the separation. This maps to Pascal's unit interface/implementation sections, but in two separate files instead of one.
Qualified vs unqualified imports
{ Pascal: uses makes all names directly visible } program Demo; uses SysUtils; begin writeln(IntToStr(42)); { unqualified } end.
(* Unqualified: FROM Module IMPORT Name — matches Pascal uses *) (* FROM STextIO IMPORT WriteString, WriteLn; *) (* WriteString("hello"); *) (* Qualified: IMPORT Module — names need Module.Name prefix *) (* IMPORT STextIO; *) (* STextIO.WriteString("hello"); *) MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; (* unqualified *) BEGIN WriteString("hello"); WriteLn; END Example.
FROM Module IMPORT Name brings names into scope directly — like Pascal's uses. IMPORT Module keeps names qualified with Module.Name. Qualified imports are safer when two modules export the same name.
Records & Pointers
Record types
program Demo; type Point = record x, y: Integer; end; var origin: Point; begin origin.x := 3; origin.y := 4; writeln(origin.x, ' ', origin.y); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; TYPE Point = RECORD x, y: INTEGER; END; VAR origin: Point; BEGIN origin.x := 3; origin.y := 4; WriteInt(origin.x, 0); WriteString(" "); WriteInt(origin.y, 0); WriteLn; END Example.
Record syntax is nearly identical in both languages — only uppercase keywords and a closing END (instead of Pascal's end;). Field access uses the same dot notation.
Record assignment
program Demo; type Point = record x, y: Integer; end; var p1, p2: Point; begin p1.x := 3; p1.y := 4; p2 := p1; { copies all fields } p2.x := 99; writeln(p1.x, ' ', p2.x); { 3 99 } end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; TYPE Point = RECORD x, y: INTEGER; END; VAR p1, p2: Point; BEGIN p1.x := 3; p1.y := 4; p2 := p1; (* copies all fields *) p2.x := 99; WriteInt(p1.x, 0); WriteString(" "); WriteInt(p2.x, 0); WriteLn; (* 3 99 *) END Example.
Both Modula-2 and Pascal copy all record fields with a single := assignment. This is a value copy — modifying p2 after the assignment does not affect p1.
Pointer types
program Demo; type IntPtr = ^Integer; var ptr: IntPtr; begin New(ptr); ptr^ := 42; writeln(ptr^); Dispose(ptr); end.
MODULE Example; FROM STextIO IMPORT WriteLn; FROM SWholeIO IMPORT WriteInt; FROM Storage IMPORT ALLOCATE, DEALLOCATE; TYPE IntPtr = POINTER TO INTEGER; VAR ptr: IntPtr; BEGIN NEW(ptr); ptr^ := 42; WriteInt(ptr^, 0); WriteLn; DISPOSE(ptr); END Example.
Modula-2 uses POINTER TO T instead of Pascal's ^T. The ^ dereference operator is the same. Crucially, Storage must be imported for NEW/DISPOSE to work — without it, the compiler reports "ALLOCATE procedure not found for NEW substitution".
Linked list node
program Demo; type NodePtr = ^Node; Node = record { note: var can't be named "node" -- } value: Integer; { Pascal is case-insensitive! } next : NodePtr; end; var head, second: NodePtr; begin New(head); head^.value := 1; head^.next := nil; New(second); second^.value := 2; second^.next := head; writeln(second^.value, ' -> ', head^.value); Dispose(second); Dispose(head); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; FROM Storage IMPORT ALLOCATE, DEALLOCATE; TYPE NodePtr = POINTER TO Node; Node = RECORD value: INTEGER; next : NodePtr; END; VAR head, node: NodePtr; BEGIN NEW(head); head^.value := 1; head^.next := NIL; NEW(node); node^.value := 2; node^.next := head; WriteInt(node^.value, 0); WriteString(" -> "); WriteInt(head^.value, 0); WriteLn; DISPOSE(node); DISPOSE(head); END Example.
NIL is uppercase in Modula-2. Forward pointer references work the same way as in Pascal — you can declare NodePtr = POINTER TO Node before Node is fully defined.
Sets
Set types
program Demo; type Fruit = (Apple, Banana, Cherry); FruitSet = set of Fruit; var basket: FruitSet; begin basket := [Apple, Cherry]; if Apple in basket then writeln('Has apple'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; TYPE Fruit = (Apple, Banana, Cherry); FruitSet = SET OF Fruit; VAR basket: FruitSet; BEGIN basket := FruitSet{Apple, Cherry}; IF Apple IN basket THEN WriteString("Has apple"); WriteLn; END; END Example.
Set syntax is nearly identical — only uppercase keywords and the type-prefixed constructor FruitSet{Apple, Cherry} differ from Pascal's bare [Apple, Cherry]. The IN membership operator is the same.
Set operations
program Demo; type Fruit = (Apple, Banana, Cherry); FruitSet = set of Fruit; var a, b, combined: FruitSet; begin a := [Apple, Banana]; b := [Banana, Cherry]; combined := a + b; { union } if Cherry in combined then writeln('Has cherry'); combined := a - b; { difference } if not (Banana in combined) then writeln('No banana'); combined := a * b; { intersection } if Banana in combined then writeln('Banana in both'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; TYPE Fruit = (Apple, Banana, Cherry); FruitSet = SET OF Fruit; VAR a, b, combined: FruitSet; BEGIN a := FruitSet{Apple, Banana}; b := FruitSet{Banana, Cherry}; combined := a + b; (* union *) IF Cherry IN combined THEN WriteString("Has cherry"); WriteLn; END; combined := a - b; (* difference *) IF NOT (Banana IN combined) THEN WriteString("No banana"); WriteLn; END; combined := a * b; (* intersection *) IF Banana IN combined THEN WriteString("Banana in both"); WriteLn; END; END Example.
Set union (+), difference (-), and intersection (*) use the same operators in both languages. This is one of the areas where Modula-2 and Pascal are virtually identical.
Adding and removing elements
program Demo; type Fruit = (Apple, Banana, Cherry); FruitSet = set of Fruit; var basket: FruitSet; begin basket := []; Include(basket, Apple); Include(basket, Banana); Exclude(basket, Apple); if Banana in basket then writeln('Has banana'); if not (Apple in basket) then writeln('No apple'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; TYPE Fruit = (Apple, Banana, Cherry); FruitSet = SET OF Fruit; VAR basket: FruitSet; BEGIN basket := FruitSet{}; INCL(basket, Apple); INCL(basket, Banana); EXCL(basket, Apple); IF Banana IN basket THEN WriteString("Has banana"); WriteLn; END; IF NOT (Apple IN basket) THEN WriteString("No apple"); WriteLn; END; END Example.
Modula-2's built-in INCL and EXCL procedures add and remove elements from a set variable. Pascal's Include/Exclude procedures (Free Pascal) serve the same purpose. The empty set is FruitSet{} instead of Pascal's [].
Error Handling
No exception handling
program Demo; uses SysUtils; begin try raise Exception.Create('something went wrong'); except on E: Exception do writeln('Caught: ', E.Message); end; end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; (* ISO Modula-2 has no exception handling mechanism. *) (* Errors are communicated via return codes or VAR *) (* parameters. HALT terminates on unrecoverable *) (* errors. There is no try/except/finally. *) BEGIN WriteString("No exceptions in Modula-2"); WriteLn; END Example.
Standard ISO Modula-2 has no exception handling at all. Programs must handle errors through return codes, BOOLEAN out-parameters, or HALT. This is a significant departure from modern Pascal (FPC) and requires a different design mindset.
Error handling with return codes
program Demo; uses SysUtils; function SafeDivide(a, b: Integer; out quotient: Integer): Boolean; begin if b = 0 then Exit(False); quotient := a div b; Exit(True); end; var quotient: Integer; begin if SafeDivide(10, 3, quotient) then writeln(quotient) else writeln('Error: div by zero'); end.
MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; FROM SWholeIO IMPORT WriteInt; PROCEDURE SafeDivide(a, b: INTEGER; VAR quotient: INTEGER): BOOLEAN; BEGIN IF b = 0 THEN RETURN FALSE; END; quotient := a DIV b; RETURN TRUE; END SafeDivide; VAR quotient: INTEGER; BEGIN IF SafeDivide(10, 3, quotient) THEN WriteInt(quotient, 0); WriteLn; ELSE WriteString("Error: div by zero"); WriteLn; END; END Example.
The standard Modula-2 error-handling pattern is to return BOOLEAN and pass the result via a VAR parameter. This is the same approach used in Go and C. Integer division uses DIV (not /), which is the same as Pascal.
Terminating on fatal error
program Demo; begin writeln('About to halt'); Halt(1); writeln('Never reached'); end.
(* HALT from SYSTEM is not available in all Modula-2 *) (* implementations on Compiler Explorer. The concept *) (* is shown in comments; the runnable example *) (* demonstrates normal termination instead. *) MODULE Example; FROM STextIO IMPORT WriteString, WriteLn; BEGIN WriteString("Demonstrating normal exit"); WriteLn; (* HALT; would terminate abnormally here *) END Example.
In ISO Modula-2, HALT is a standard environment procedure that terminates the program immediately — typically imported from SYSTEM. Unlike Pascal's Halt(n), ISO HALT takes no exit code argument. Reserve it for truly unrecoverable situations; structured programs prefer returning error codes.