Hello World in PL/I
Your first PL/I program - the classic Hello World example with Docker setup using Iron Spring PL/I compiler
Every programming journey starts with Hello World. Let’s write our first PL/I program using the Iron Spring PL/I compiler - a modern, free compiler that brings this classic mainframe language to contemporary systems.
The Code
Create a file named hello.pli:
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello, World!');
END HELLO;
Yes, that’s it! PL/I’s Hello World is remarkably concise for a language from the 1960s.
Understanding the Code
Let’s break down each element of this simple program:
HELLO:- A label naming the procedure (optional but conventional)PROCEDURE OPTIONS(MAIN);- Declares this as the main entry point procedurePUT LIST('Hello, World!');- Outputs the text string to standard outputEND HELLO;- Closes the procedure (label reference is optional but good practice)
The PROCEDURE Block
PL/I programs are structured as procedures:
name: PROCEDURE OPTIONS(MAIN);
/* declarations and statements */
END name;
The OPTIONS(MAIN) tells the compiler this procedure is the program’s entry point - where execution begins.
PUT LIST Statement
PUT LIST is PL/I’s flexible output statement:
PUT LIST(expression1, expression2, ...);
It automatically:
- Converts values to strings
- Adds spacing between items
- Handles different data types
The LIST option provides list-directed output - simple and human-readable.
Semicolons and Statements
PL/I uses semicolons to terminate statements:
PUT LIST('Hello'); /* Statement ends with ; */
X = 10; /* Another statement */
Multiple statements can appear on one line or span multiple lines - whitespace is flexible.
Running with Docker
The easiest way to run PL/I without installing a compiler locally uses our custom Docker image with the Iron Spring PL/I compiler:
| |
Understanding the Commands
Docker Build (one-time setup):
docker build- Creates a container image-t codearchaeology/pli:latest- Tags the image.- Build context (current directory with Dockerfile)
Compile and Run:
--security-opt seccomp=unconfined- Required for the 32-bit PL/I compiler on Docker Desktopplicc hello.pli- Wrapper script that compiles and links PL/I programs./hello- Runs the compiled executable
The plicc wrapper script:
- Compiles the source code with
plic -C -dELF - Links with the PL/I runtime library using
ld - Produces an executable named
hello(based on source filename)
Expected Output
Hello, World!
Simple and clean - exactly what we expect!
Alternative Dockerfile Approach
If you prefer to keep everything contained, create a simpler test Dockerfile:
| |
Build and run:
| |
Running Locally (Linux)
If you have Linux and want to install Iron Spring PL/I directly:
| |
Tested Platforms
Iron Spring PL/I is tested on:
- Ubuntu 24.04 (Noble Numbat) - Officially tested
- Ubuntu 22.04 (Jammy Jellyfish) - Works
- Debian 12 - Works
- Fedora 39+ - Works
- Windows WSL2 - Works with Ubuntu distribution
Program Structure Variations
PL/I is flexible - here are valid alternatives:
Without Label
PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello, World!');
END;
The label is optional (though good practice for larger programs).
With DCL for Declarations
HELLO: PROCEDURE OPTIONS(MAIN);
DCL MESSAGE CHAR(20);
MESSAGE = 'Hello, World!';
PUT LIST(MESSAGE);
END HELLO;
DCL (or DECLARE) introduces variable declarations.
Using PUT SKIP
HELLO: PROCEDURE OPTIONS(MAIN);
PUT SKIP LIST('Hello, World!');
END HELLO;
PUT SKIP adds a newline before output (useful for formatting).
Multiple Statements
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello,');
PUT LIST(' World!');
END HELLO;
Output: Hello, World! (LIST automatically spaces items)
PUT STRING for Precise Control
HELLO: PROCEDURE OPTIONS(MAIN);
PUT STRING('Hello, World!');
END HELLO;
PUT STRING writes exactly the characters specified without extra spacing.
Understanding PUT Variants
PL/I offers several output methods:
PUT LIST (List-Directed)
PUT LIST('Text', 42, 3.14);
Automatic formatting, spacing between items.
PUT EDIT (Edit-Directed)
PUT EDIT('Hello, World!')(A);
Format control - (A) means character format.
PUT DATA
DCL NAME CHAR(20);
NAME = 'Ada Lovelace';
PUT DATA(NAME);
Outputs: NAME='Ada Lovelace'; - variable name and value.
PUT SKIP
PUT SKIP LIST('First line');
PUT SKIP LIST('Second line');
SKIP adds newlines for formatting.
Character Strings in PL/I
PL/I handles strings flexibly:
String Literals
'Hello, World!' /* Single quotes */
"Hello, World!" /* Double quotes also work */
String Variables
DCL MESSAGE CHAR(50);
MESSAGE = 'Hello, World!';
CHAR(50) - Fixed-length character string (50 characters).
Varying Strings
DCL MESSAGE CHAR(50) VARYING;
MESSAGE = 'Hello, World!'; /* Only uses 13 characters */
VARYING - Variable-length string (more memory-efficient).
Comments in PL/I
PL/I uses C-style block comments:
/* This is a comment */
/*
This is a
multi-line comment
*/
HELLO: PROCEDURE OPTIONS(MAIN);
/* Main procedure starts here */
PUT LIST('Hello, World!'); /* Output greeting */
END HELLO; /* End of program */
Case Sensitivity
PL/I is case-insensitive (unlike C):
PROCEDURE /* Same as */
procedure /* Same as */
Procedure
Convention: Use UPPERCASE for keywords, though mixed case works fine.
File Extensions
PL/I programs use various extensions:
.pli- Most common (Iron Spring, modern).pl1- Traditional mainframe.plior.plx- Various compilers
Iron Spring PL/I accepts .pli, .pl1, and .plx.
Common Beginner Mistakes
Missing Semicolons
PUT LIST('Hello') /* ❌ Missing semicolon */
PUT LIST('Hello'); /* ✅ Correct */
Forgetting OPTIONS(MAIN)
PROCEDURE; /* ❌ Not the main procedure */
PROCEDURE OPTIONS(MAIN); /* ✅ Correct for entry point */
Mismatched Labels
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello, World!');
END GOODBYE; /* ❌ Label doesn't match */
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello, World!');
END HELLO; /* ✅ Correct */
Wrong Quote Types in Some Contexts
While PL/I accepts both ' and ", be consistent:
PUT LIST('Hello, World!'); /* ✅ Consistent */
PUT LIST("Hello, World!"); /* ✅ Also works */
PUT LIST('Hello, World!"); /* ❌ Mismatched quotes */
Compiler Options
Iron Spring PL/I compiler (plic) supports various options:
| |
Listing Files
Generate compilation listing:
| |
Creates hello.lst with compilation details.
Preprocessor Output
See preprocessed source:
| |
PL/I Preprocessor
PL/I includes a powerful preprocessor:
%DECLARE DEBUG BIT;
%DEBUG = '1'B;
HELLO: PROCEDURE OPTIONS(MAIN);
%IF DEBUG %THEN
PUT LIST('Debug: Starting program');
%ENDIF;
PUT LIST('Hello, World!');
%IF DEBUG %THEN
PUT LIST('Debug: Ending program');
%ENDIF;
END HELLO;
The % prefix indicates preprocessor statements - executed at compile time.
Advanced Hello World Variations
With Declarations Section
HELLO: PROCEDURE OPTIONS(MAIN);
DECLARE
MESSAGE CHARACTER(20) VARYING,
I FIXED BINARY;
MESSAGE = 'Hello, World!';
PUT LIST(MESSAGE);
END HELLO;
Structured declaration section for larger programs.
With BEGIN Block
HELLO: PROCEDURE OPTIONS(MAIN);
BEGIN;
DECLARE MESSAGE CHAR(20);
MESSAGE = 'Hello, World!';
PUT LIST(MESSAGE);
END;
END HELLO;
BEGIN/END creates nested scope blocks.
With Error Handling
HELLO: PROCEDURE OPTIONS(MAIN);
ON ERROR SYSTEM; /* Use system error handler */
PUT LIST('Hello, World!');
END HELLO;
PL/I’s exception handling - even in Hello World!
With Multiple Outputs
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello,');
PUT LIST(' World!');
PUT SKIP;
PUT LIST('Welcome to PL/I programming.');
END HELLO;
Demonstrates multiple output statements and SKIP for newlines.
Compilation Process
Understanding what happens when you compile:
hello.pli → hello.o (Object file)
→ hello (Executable)
Separate Compilation Steps
You can compile in stages:
| |
PL/I’s OUTPUT Philosophy
PL/I separates data from formatting:
- PUT LIST - Automatic formatting
- PUT EDIT - Controlled formatting
- PUT DATA - Debug output with variable names
- PUT STRING - Exact output
This flexibility was revolutionary in 1964.
Why PL/I’s Hello World Matters
Even this simple program shows PL/I’s character:
- Structured - PROCEDURE blocks enforce organization
- Labeled - Optional labels improve readability
- Flexible - Multiple ways to achieve the same result
- Powerful - Built-in I/O, no #include needed
- Self-Documenting - OPTIONS(MAIN) makes intent clear
These design choices influenced later languages (Ada, C++, Java).
Historical Context
In 1964, when PL/I was designed:
- FORTRAN - Used FORMAT statements (complex)
- COBOL - Used DISPLAY (verbose but readable)
- Assembly - Used OS system calls (low-level)
PL/I’s PUT LIST was simpler than FORTRAN, more flexible than COBOL, and higher-level than assembly.
Comparing to C
C’s Hello World (1972):
| |
PL/I’s version (1964):
HELLO: PROCEDURE OPTIONS(MAIN);
PUT LIST('Hello, World!');
END HELLO;
PL/I is arguably cleaner - no includes, no explicit return.
The Mainframe Connection
On IBM mainframes, Hello World looks similar:
HELLO: PROC OPTIONS(MAIN);
PUT SKIP LIST('Hello, World!');
END HELLO;
Iron Spring PL/I maintains high compatibility with IBM mainframe PL/I.
Next Steps
Now that you’ve written your first PL/I program, explore:
- Variables and Data Types - PL/I’s rich type system
- Control Structures - IF, DO loops, SELECT
- Procedures and Functions - Modular programming
- File I/O - Working with datasets
- Structures - Complex data organization
Key Takeaways
- PL/I programs are procedures marked with
OPTIONS(MAIN) - PUT LIST provides simple output without format complexity
- Semicolons terminate statements (like C, unlike FORTRAN)
- Labels are optional but improve code clarity
- PL/I is case-insensitive (unlike C)
- Iron Spring PL/I brings PL/I to modern platforms for free
- Docker makes PL/I accessible without complex installation
Troubleshooting
“plic: command not found”
You need to install Iron Spring PL/I or use Docker:
| |
“SIGACTION returned -1” error
This occurs when running the 32-bit PL/I compiler on Docker Desktop (macOS/Windows). Add the security option:
| |
“Cannot find library”
Set the library path:
| |
Or use Docker which handles this automatically.
Compilation Errors
Check for:
- Missing semicolons
- Mismatched quotes
- Mismatched procedure labels
- OPTIONS(MAIN) on entry procedure
“Permission denied” when running
Make executable:
| |
Resources
- Iron Spring PL/I - www.iron-spring.com (compiler download, docs)
- IBM PL/I Documentation - IBM Knowledge Center (comprehensive reference)
- comp.lang.pl1 - Usenet newsgroup (active community)
- Multics PL/I - multicians.org/pl1.html (historical perspective)
The Big Picture
This simple Hello World connects you to:
- 1960s IBM mainframe computing
- The Multics operating system (Unix’s predecessor)
- Decades of mission-critical systems still running today
- Language design history that influenced C, C++, and Java
You’re not just printing a string - you’re experiencing computing history.
Welcome to PL/I programming!
Running Today
All examples can be run using Docker:
docker pull codearchaeology/pli:latest