Hello World in Modula-2
Your first Modula-2 program - the classic Hello World example with Docker setup using GNU Modula-2 (gm2)
Every programming journey starts with Hello World. Let’s write our first Modula-2 program using the GNU Modula-2 compiler (gm2), which is now part of GCC.
The Code
Create a file named hello.mod:
| |
Understanding the Code
Modula-2 programs are clean, modular, and structured. Let’s break down each part:
MODULE Hello;- Declares a module named Hello (the program entry point)FROM StrIO IMPORT WriteString, WriteLn;- Imports specific procedures from the StrIO libraryBEGIN- Marks the start of the module body (executable statements)WriteString("Hello, World!");- Outputs the text stringWriteLn- Outputs a newline characterEND Hello.- Closes the module (must match the module name and end with a period)
The Import Statement
Modula-2 uses qualified imports to control the namespace:
| |
This imports only WriteString and WriteLn from the StrIO module, making them available directly. You could also write:
| |
This keeps the module prefix, making the code origin explicit.
Module Structure
A Modula-2 program module has this structure:
| |
Notice the period after Name - program modules end with . while definition and implementation modules end with just the module name.
Case Sensitivity
Unlike Pascal, Modula-2 is case-sensitive:
MODULEis different frommodule(use uppercase for keywords)Hellois different fromhello- Convention: Keywords in uppercase, identifiers in MixedCase
Comments
Modula-2 uses Pascal-style comments:
| |
Running with Docker
The easiest way to run Modula-2 without installing gm2 locally uses GCC (which includes gm2 since version 13) in a Docker container:
| |
Understanding the Docker Command
gm2- The GNU Modula-2 compiler (part of GCC)-o hello- Output executable namedhellohello.mod- Input source file (.mod = Modula-2 module)./hello- Run the compiled executable
The gm2 command automatically:
- Compiles the source to object code
- Links with the Modula-2 runtime library
- Produces an executable
Running Locally
If you have GCC 13+ with gm2 installed:
| |
Installing gm2
macOS (Homebrew):
| |
Ubuntu/Debian (GCC 13+):
| |
Fedora/RHEL:
| |
From Source:
| |
Expected Output
Hello, World!
Clean and simple - exactly what you’d expect!
Alternative Approaches
Modula-2’s standard library offers several ways to output text:
Using Individual Character Output
| |
This demonstrates arrays and loops, but WriteString is much simpler.
Using Write Procedures
| |
Write outputs a single character, WriteString outputs a string.
With Constants
| |
Constants declared between imports and BEGIN.
With Variables
| |
Variables declared in the VAR section.
The .mod and .def Files
Modula-2 programs use two file types:
.mod (Module Implementation)
Contains:
- Program modules (like our Hello World)
- Implementation modules (code for definition modules)
- Module initialization code
.def (Definition Module)
Contains:
- Module interfaces (exported types, procedures, constants)
- Type declarations visible to clients
- Procedure signatures (no implementation)
Our simple Hello World only needs a .mod file since it’s a standalone program module. As programs grow, you’ll create libraries with separate definition (.def) and implementation (.mod) files.
Program Structure
A minimal Modula-2 program requires:
- MODULE declaration - Define the program name
- Optional IMPORT clauses - Bring in needed modules
- Optional declarations - Constants, types, variables, procedures
- BEGIN keyword - Start module body
- Statements - Actual program logic
- END clause - Close the module with name and period
Example with Declarations
| |
Notice:
- Constants declared in
CONSTsection - Variables declared in
VARsection - Strings are character arrays
- Each section appears before
BEGIN
Compilation Process
When you run gm2 -o hello hello.mod, several things happen:
hello.mod → hello.o (Object file)
→ hello (Executable)
Separate Compilation Steps
You can also compile manually:
| |
With Multiple Modules
For larger programs:
| |
Common Beginner Mistakes
Missing Period After Module Name
| |
Wrong Import Syntax
| |
Mismatched Module Name
| |
Forgetting Semicolons
| |
Case Errors
| |
Wrong File Extension
Modula-2 requires specific extensions:
hello.m2- ❌ Not standardhello.m- ❌ Not recognized by gm2hello.mod- ✅ Correct for moduleshello.def- ✅ Correct for definitions
Compiler Options
Useful gm2 compilation flags:
| |
Dialect Selection
gm2 supports multiple Modula-2 dialects:
| |
Our Hello World works with all dialects.
Standard Library Modules
Common Modula-2 library modules:
InOut
Input and output for basic types:
WriteString()- Output stringWriteInt()- Output integerWriteLn- Output newlineReadString()- Input stringReadInt()- Input integer
Storage
Dynamic memory allocation:
ALLOCATE()- Allocate memoryDEALLOCATE()- Free memory
Strings
String manipulation:
Length()- String lengthConcat()- Concatenate stringsCompare()- Compare strings
MathLib0
Mathematical functions:
sqrt()- Square rootsin(),cos()- Trigonometricexp(),ln()- Exponential/logarithm
Why Modula-2 for Hello World?
Even in this simple example, you see Modula-2’s philosophy:
- Explicit Imports - No hidden dependencies or globals
- Clear Structure - MODULE declaration, BEGIN/END blocks
- Module Name Redundancy -
END Hello.catches copy-paste errors - Qualified Access -
InOut.WriteStringshows exactly where functions come from - Strong Typing - Even strings have explicit types (character arrays)
These features become invaluable in large systems where Modula-2 was designed to shine.
A Bit of History
Modula-2 was created by Niklaus Wirth (1934-2024), who also designed Pascal. The name “Modula” comes from modular programming - the language’s central concept.
In 1978, when Modula-2 emerged, most systems programming was done in assembly or C. Modula-2 proved you could write operating systems, compilers, and device drivers in a high-level language with strong type safety. The entire Lilith workstation OS was written in Modula-2.
Today, Modula-2’s module system lives on in Go (designed by Wirth’s student), Rust, and even modern JavaScript/TypeScript modules.
Performance Note
Modula-2 compiles to efficient machine code. The gm2 compiler uses GCC’s backend, producing optimized executables comparable to C. The strong typing and safety checks happen at compile time, not runtime - Modula-2 programs run at full speed.
Standard Output Libraries
Different Modula-2 implementations have different I/O modules:
PIM Style (our example)
| |
ISO Style
| |
Terminal Module
| |
gm2 supports all these - use -fpim or -fiso to select the dialect.
Next Steps
Now that you’ve written your first Modula-2 program, you can explore:
- Variables and Data Types - Learn about Modula-2’s strong type system
- Module System - Create reusable definition and implementation modules
- Pointers and Dynamic Memory - Work with dynamic data structures
- Procedures and Functions - Build modular, reusable code
Key Takeaways
- Modula-2 uses explicit imports via
FROM module IMPORTclauses - Programs are modules with
MODULE/BEGIN/ENDstructure - Module names must match in the END clause with a period
- Case sensitive - keywords in uppercase, identifiers in MixedCase
- gm2 is part of GCC and available in modern GCC distributions
.modfiles contain program and implementation modules.deffiles contain definition modules (interfaces)- Comments use
(* ... *)delimiters
Troubleshooting
“gm2: command not found”
Your GCC version is older than 13. Either:
- Install GCC 13+ from your package manager
- Build GCC from source with
--enable-languages=m2 - Use Docker with
gcc:latestimage
“cannot find module ‘InOut’”
Specify the PIM dialect:
| |
“syntax error”
Check:
- Keywords are UPPERCASE (
MODULE, notmodule) - Module name matches in
END Hello. - Period after module name in END clause
- Semicolons after IMPORT and before BEGIN
Strange compilation errors
Try explicitly setting the dialect:
| |
Running Today
All examples can be run using Docker:
docker pull codearchaeology/modula-2:latest