I/O Operations in ALGOL 68
Console output, reading input, formatted output with printf, and real file I/O through channels in ALGOL 68 - all runnable with Algol 68 Genie and Docker
Introduction
Where ALGOL 60 deliberately left input and output undefined, ALGOL 68 went the opposite
way and built a complete, carefully specified transput model directly into the
language (the ALGOL 68 Report uses the word transput for the combined idea of transput =
transfer + in/output). This model is unusually rich for a language of its era: it
defines files, channels that connect files to devices, automatic formatting of every
mode, and a printf-style format facility - all standardised rather than left to each
implementation.
In the Hello World tutorial you used print to send text to the screen and saw printf
in passing. This tutorial uses the full toolkit. You will print values of every basic mode
with print, turn numbers into neat strings with the whole and fixed formatting
functions, read values typed at the terminal with read, lay out reports with printf and
its $...$ format patterns, and finally write and read a genuine named file through
ALGOL 68’s channel system - something ALGOL 60 never offered.
Because ALGOL 68 is an imperative, procedural language, transput reads as a clear sequence
of procedure calls. What makes it distinctive is the orthogonality: the same print,
put, and get procedures work uniformly whether the value is an INT, a REAL, a
BOOL, a CHAR, or a STRING, and whether the destination is the screen or a file.
Console Output for Every Mode
print takes a single value, or a structure of values written in parentheses, and sends
each to standard output. The catch is that printing a number with bare print uses a
default field width and a leading sign, which is rarely what you want for tidy output. The
fix is the formatting functions whole(value, width) for integers and
fixed(value, width, after) for reals: with a width of 0 they return the shortest
faithful string, with no padding and no + sign.
Create a file named io_output.a68:
BEGIN
# print sends any mode to standard output; whole and fixed turn #
# numbers into clean, padding-free strings (width 0 = shortest form) #
INT count = 42;
REAL pi = 3.14159;
BOOL active = TRUE;
CHAR grade = "A";
print(("ALGOL 68 transput", new line));
print(("Count : ", whole(count, 0), new line));
print(("Pi : ", fixed(pi, 0, 4), new line));
# A BOOL prints as T or F by default, so map it to readable text #
print(("Active: ", (active | "yes" | "no"), new line));
print(("Grade : ", grade, new line))
END
The conditional expression (active | "yes" | "no") is ALGOL 68’s compact if-then-else: it
yields "yes" when active is TRUE. Note how fixed(pi, 0, 4) rounds to exactly four
decimal places, giving you the decimal control that ALGOL 60’s shortest-form output could
never provide.
Reading Input
Input mirrors output. read takes a structure of variables (not values) and fills each
one from standard input. Reading an INT skips leading spaces and newlines, then consumes
digits; reading a STRING consumes the rest of the current line; and the new line item
inside a read list advances past the line terminator so the next read starts cleanly.
Create a file named io_input.a68:
BEGIN
# read fills variables from standard input. STRING reads to end of #
# line; new line in the list then steps past the line terminator #
print(("Enter your name: "));
STRING name;
read((name, new line));
print(("Enter your age: "));
INT age;
read((age, new line));
print(("Hello, ", name, "!", new line));
print(("Next year you will be ", whole(age + 1, 0), new line))
END
Because the program reads from standard input, you supply the values by piping them in (shown in the Docker section). Piped input is not echoed back, so each prompt sits on the same line as the response the program prints next.
Formatted Output with printf
For report-style output, printf pairs a format - the $...$ pattern - with the values
to fill it. Format patterns use single-letter items: g inserts a value in its general
form, l emits a newline, and literal text in quotes is printed verbatim. A clean,
predictable approach is to pre-format numbers with whole and fixed, then drop the
resulting strings into g placeholders.
Create a file named io_printf.a68:
BEGIN
# printf marries a $...$ format with values. g inserts a value, #
# l is a newline. Pre-format numbers with whole/fixed for control #
INT items = 7;
REAL price = 19.99;
REAL total = items * price;
printf(($g" units at $"g" each"l$,
whole(items, 0), fixed(price, 0, 2)));
printf(($"Total cost: $"gl$,
fixed(total, 0, 2)));
# printf can also take several values against repeated format items #
printf(($"Line "g": "gl$, whole(1, 0), "first"));
printf(($"Line "g": "gl$, whole(2, 0), "second"))
END
Multiplying the INT items by the REAL price widens the integer to a real
automatically - one of ALGOL 68’s coercions - so total is a REAL and fixed can round
it to two decimal places.
File I/O Through Channels
This is where ALGOL 68 pulls decisively ahead of ALGOL 60. A FILE is a variable that
represents an open stream, and you attach it to a named disk file through a channel that
says what kind of access you want. Creating a brand-new file for writing uses establish
with the stand out channel; opening an existing file for reading uses open with the
stand in channel. (open expects the file to already exist, which is why writing goes
through establish.) Both return an INT that is 0 on success. Once attached, the same
put and get procedures mirror print and read, but aimed at the file. Always close
the file when done.
The program below writes a small report to a real file and then reopens and reads it back, so a single run is fully self-contained.
Create a file named io_files.a68:
BEGIN
# ----- Write a report to a named file ----- #
# establish creates a new file for writing; open (used below) #
# only attaches to a file that already exists #
FILE out file;
IF establish(out file, "report.txt", stand out channel) /= 0 THEN
print(("Cannot open report.txt for writing", new line));
stop
FI;
put(out file, ("Sales Report", new line));
put(out file, ("Widgets sold: ", whole(150, 0), new line));
put(out file, ("Revenue: $", fixed(2998.50, 0, 2), new line));
close(out file);
print(("Wrote 3 lines to report.txt", new line));
# ----- Reopen the same file and read it back ----- #
FILE in file;
IF open(in file, "report.txt", stand in channel) /= 0 THEN
print(("Cannot open report.txt for reading", new line));
stop
FI;
STRING line;
TO 3 DO
get(in file, (line, new line));
print(("> ", line, new line))
OD;
close(in file)
END
get(in file, (line, new line)) reads one line into the flexible STRING line, then
steps past the line break, and the TO 3 DO ... OD loop repeats that three times - once per
line we wrote. The result file, report.txt, is left on disk in your working directory
because the -v $(pwd):/app mount maps the container’s /app back to your host folder.
Running with Docker
Use the same Algol 68 Genie image as the rest of this series. Pull it once:
| |
Output-only programs run exactly like Hello World:
| |
The input program needs the -i flag so Docker keeps standard input connected. Pipe two
lines in - a name then an age:
| |
Expected Output
io_output.a68:
ALGOL 68 transput
Count : 42
Pi : 3.1416
Active: yes
Grade : A
io_printf.a68:
7 units at $19.99 each
Total cost: $139.93
Line 1: first
Line 2: second
io_input.a68, given the piped input Ada then 35:
Enter your name: Hello, Ada!
Enter your age: Next year you will be 36
io_files.a68:
Wrote 3 lines to report.txt
> Sales Report
> Widgets sold: 150
> Revenue: $2998.50
After the file example runs, report.txt remains in your working directory containing the
three written lines.
Key Concepts
- Transput is built in. Unlike ALGOL 60, ALGOL 68 standardised input and output - called transput - directly in the language, so the same code behaves consistently across conforming implementations like Algol 68 Genie.
- One set of verbs, every mode.
printandreadhandleINT,REAL,BOOL,CHAR, andSTRINGuniformly; you pass a structure of values in parentheses, e.g.print(("x = ", whole(x, 0), new line)). - Format numbers with
wholeandfixed.whole(n, 0)gives an integer as its shortest string andfixed(x, 0, d)gives a real rounded toddecimals - both with no padding or+sign at width0. readtakes variables, not values.read((name, new line))fillsnameand then steps past the line terminator; numeric reads skip leading whitespace automatically.printfjoins a format to values. The$...$pattern usesgto insert a value andlfor a newline; pre-formatting numbers withwhole/fixedkeeps output predictable.- Files go through channels.
establish(file, "name", stand out channel)creates a new file for writing andopen(file, "name", stand in channel)attaches to an existing one for reading - both return0on success; thenput/getmirrorprint/read, andclosefinishes - genuine named-file I/O that ALGOL 60 lacked entirely. - Coercion widens automatically. Multiplying an
INTby aREALpromotes the integer, so arithmetic mixing the two modes yields aREALwithout an explicit cast. - Conditional expressions are values.
(flag | "yes" | "no")is ALGOL 68’s compact if-then-else and can be dropped straight into aprintlist.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/algol68:latest
Comments
Loading comments...
Leave a Comment