Variables and Types in Modula-2
Learn about variables, data types, constants, and type conversions in Modula-2 with practical Docker-ready examples
Modula-2’s type system is one of its defining features. Designed by Niklaus Wirth as an improvement over Pascal, Modula-2 enforces strict, static typing that catches errors at compile time rather than letting them slip through to runtime. Every variable must be declared with an explicit type before use, and the compiler enforces compatibility rules that prevent entire classes of bugs.
As an imperative, procedural language with strong typing, Modula-2 requires all variables to be declared in a VAR section before the BEGIN block. This separation of declarations from executable code is intentional — it makes the data a program operates on immediately visible at a glance. The type system distinguishes between integers and cardinals (unsigned integers), provides enumeration and subrange types for constraining values, and treats strings as arrays of characters.
In this tutorial, you’ll learn how to declare variables and constants, work with Modula-2’s built-in types, define your own types, and perform type conversions — all foundational skills for writing safe, correct Modula-2 programs.
Basic Types and Variable Declarations
Modula-2 provides several built-in types for working with numbers, characters, and boolean values. Variables are declared in the VAR section that appears between the import statements and the BEGIN block.
Create a file named variables.mod:
| |
The built-in types in Modula-2 are:
- INTEGER — signed whole numbers (positive and negative)
- CARDINAL — unsigned whole numbers (zero and positive only)
- REAL — floating-point numbers for decimal values
- CHAR — a single character, enclosed in single quotes
- BOOLEAN — either
TRUEorFALSE
Notice that Modula-2 distinguishes between INTEGER and CARDINAL. This is stricter than C, where signed/unsigned is just a modifier. In Modula-2, they are separate types with separate rules — you cannot freely mix them without explicit conversion.
Constants, Enumerations, and Custom Types
Modula-2 provides CONST for compile-time constants and TYPE for defining your own types, including enumerations and subranges. These features let you express domain constraints directly in the type system.
Create a file named types.mod:
| |
Key concepts demonstrated here:
- Constants are declared in a
CONSTsection — their values are fixed at compile time and cannot be changed. Unlike variables, constants don’t need a type annotation; the compiler infers it from the value. - Enumeration types define a set of named values.
ORD()converts an enumeration value to its ordinal position (starting at 0). - Subrange types restrict a base type to a specific range.
Score = [0..100]means anyScorevariable can only hold values from 0 to 100 — the compiler or runtime will catch violations. - Character subranges like
Initial = ['A'..'Z']restrict character variables to uppercase letters only.
Arrays and Strings
Modula-2 doesn’t have a dedicated string type. Instead, strings are represented as ARRAY OF CHAR. This is explicit and consistent with the language’s philosophy of making data representation visible.
Create a file named arrays.mod:
| |
Important points about arrays and strings in Modula-2:
- Array indices can start at any value, not just 0 —
ARRAY [1..5]starts at 1. - Strings are
ARRAY OF CHARwith a fixed maximum length declared at compile time. - String literals assigned to character arrays are automatically null-terminated if the array is longer than the string.
- Multi-dimensional arrays use nested
ARRAYdeclarations.
Type Conversions
Modula-2’s strict type system means you cannot freely mix types in expressions. The language provides built-in functions for explicit conversions between compatible types.
Create a file named conversions.mod:
| |
The key conversion functions in Modula-2 are:
VAL(Type, expr)— general-purpose conversion between ordinal typesINT(r)— converts REAL to INTEGER (truncates toward zero)FLOAT(i)— converts INTEGER to REALORD(x)— returns the ordinal position of an enumeration, character, or boolean valueCHR(n)— converts a cardinal number to its corresponding character
These explicit conversions are central to Modula-2’s safety philosophy. Rather than silently converting between types (as C does), Modula-2 forces you to state your intent, making the code’s behavior clear and preventing accidental data loss.
Running with Docker
| |
Expected Output
Variables example:
Age: 47
Count: 1024
Temperature: 36 (integer part)
Letter: M
Active: TRUE
Types example:
Application: Modula-2 Demo
Max students: 30
Day number: 2 (Wednesday)
Grade: 95
Initial: W
Monday=0 Friday=4 Sunday=6
Arrays example:
Creator: Niklaus Wirth
Numbers: 10 20 30 40 50
Matrix row 0: 1 2 3
Matrix row 1: 4 5 6
Conversions example:
Integer 42 as Cardinal: 42
Cardinal 100 as Integer: 100
Real 9.7 as Integer: 9
Integer 25 as Real: 25 (converted back)
ORD('A'): 65
CHR(90): Z
ORD(TRUE): 1
ORD(FALSE): 0
Key Concepts
- All variables must be declared in a
VARsection beforeBEGIN— Modula-2 does not allow inline declarations - INTEGER vs CARDINAL — Modula-2 separates signed and unsigned integers as distinct types, unlike C where
unsignedis a modifier - Strings are character arrays — there is no built-in string type; strings are
ARRAY OF CHARwith a fixed maximum length - Subrange types like
[0..100]constrain values at the type level, providing compile-time and runtime bounds checking - Enumeration types create named value sets with automatic ordinal numbering starting at 0
- Type conversions are always explicit — use
VAL(),INT(),FLOAT(),ORD(), andCHR()to convert between types - Constants declared with
CONSTare fixed at compile time and don’t require type annotations - The type system is the safety net — Modula-2’s strict rules catch mismatched types, out-of-range values, and accidental conversions before your program runs
Running Today
All examples can be run using Docker:
docker pull codearchaeology/modula-2:latest
Comments
Loading comments...
Leave a Comment