Algol 68 rows
last modified November 13, 2026
A row is a sequence of values of the same mode, indexed by integers. Algol 68 calls arrays rows because the concept is orthogonal: a row of integers, a row of rows of reals, and a row of characters are all built by the same rules. This article describes how a row is declared, how its bounds are read, how its elements are reached, and how rows are passed to procedures.
Declaring a row
A row denotation writes the elements in parentheses separated by commas, and an identity declaration with empty brackets infers the bounds from the number of elements. The bounds then run from 1 to the count.
BEGIN []INT nums = (10, 20, 30, 40); print((whole(LWB nums, 0), " ", whole(UPB nums, 0), newline)); print((whole(nums[1], 0), " ", whole(nums[4], 0), newline)) END
The identity declaration uses =, which means that the bounds are
fixed by the denotation and never change. Such a row is also constant: its
elements cannot be assigned, and a row that is meant to change is declared
with := and explicit bounds. LWB and
UPB return the lower and upper bound, and a subscript in brackets
selects one element. Reading nums[5] would be an error, because
the row has four elements.
$ a68g declare.a68 1 4 10 40
Explicit bounds
A row declared with := needs explicit bounds, written as
[lower:upper] before the mode name. The bounds may differ from
1 and the count, but a row denotation always has bounds 1 to its length, so
the denotation cannot initialise a row whose bounds start elsewhere.
BEGIN
[1:5] INT row := (1, 2, 3, 4, 5);
[3:7] INT shifted;
FOR i FROM LWB shifted TO UPB shifted DO
shifted[i] := i * 10
OD;
print((whole(LWB row, 0), " ", whole(UPB row, 0), newline));
print((whole(shifted[3], 0), " ", whole(shifted[7], 0), newline))
END
The first row is initialised from a denotation because its bounds are 1 to 5.
The second one starts at 3, so it is filled in a loop instead, and its
elements run from 30 to 70. Writing [3:7] INT shifted := (1, 2, 3, 4,
5) is rejected with a message about different bounds.
$ a68g bounds.a68 1 5 30 70
Walking through a row
A loop that runs from LWB to UPB visits every
element, whatever the bounds are. This pattern works for rows and for strings
alike, because both report their bounds in the same way.
BEGIN
[]INT temps = (12, 19, 7, 23, 15);
INT total := 0, largest := temps[LWB temps];
FOR i FROM LWB temps TO UPB temps DO
total +:= temps[i];
IF temps[i] > largest THEN largest := temps[i] FI
OD;
print((whole(total, 0), " ", whole(largest, 0), newline))
END
The total of the five values is 76, and the largest is 23. The variable
largest starts with the first element rather than with zero, so
the procedure also works for a row of negative numbers. An empty row would
have upper bound 0, and the loop would run zero times.
$ a68g walk.a68 76 23
Slices
A slice written as row[from:to] produces a row of its own. When
it initialises a declared row, the elements are copied; when it stands on the
left of an assignment, the assignments reach the original row.
BEGIN [1:5] INT row := (1, 2, 3, 4, 5); [1:3] INT copy := row[2:4]; copy[1] := 99; print((whole(row[2], 0), " ", whole(copy[1], 0), newline)); row[2:4] := (7, 8, 9); print((whole(row[2], 0), newline)) END
The copy holds the values 2, 3, and 4 with bounds 1 to 3. Assigning 99 to its
first element does not touch row[2], which is still 2. The second
assignment writes through the slice instead, so row[2] becomes 7.
This works because row was declared with :=: a row
initialised with = from a denotation is a constant, and its
elements cannot be assigned.
$ a68g slice.a68 2 99 7
Two-dimensional rows
Additional dimensions are written with commas inside the brackets, so
[,]INT is a row of rows of integers. The upper bound of a single
dimension is read by writing that dimension before UPB, as in
1 UPB grid.
BEGIN
[,]INT grid = ((1, 2, 3), (4, 5, 6));
print((whole(1 UPB grid, 0), " ", whole(2 UPB grid, 0), newline));
FOR i TO 1 UPB grid DO
FOR j TO 2 UPB grid DO
print((whole(grid[i, j], 0), " "))
OD;
print((newline))
OD
END
The denotation has two rows of three elements, so the first dimension has
upper bound 2 and the second has 3. A subscript pair such as
grid[2, 3] selects one element, and the nested loops print the
grid row by row. UPB grid without a dimension returns the bound
of the first dimension.
$ a68g two.a68 2 3 1 2 3 4 5 6
Rows as parameters
A procedure parameter of mode []INT accepts a row of integers
with any bounds, and the procedure reads those bounds from its parameter.
This makes the same procedure usable for rows that start at 1 and for rows
that start somewhere else.
PROC total = ([]INT row) INT:
( INT sum := 0;
FOR i FROM LWB row TO UPB row DO sum +:= row[i] OD;
sum );
BEGIN
[]INT a = (1, 2, 3);
[10:12] INT b;
FOR i FROM LWB b TO UPB b DO b[i] := i - 6 OD;
print((whole(total(a), 0), " ", whole(total(b), 0), newline))
END
The first call passes a row with bounds 1 to 3 and gets 6. The second row has bounds 10 to 12 and holds the values 4, 5, and 6, so the same loop sums it to 15. Had the loop counted from 1 to 3, it would have read two elements outside the row and missed one inside it.
$ a68g procarg.a68 6 15
Rows of strings
The element mode of a row may be any mode, including STRING. A
row of strings is declared like any other row, and a subscript yields one
string.
BEGIN
[]STRING names = ("Alice", "Bob", "Carol");
FOR i TO UPB names DO
print((whole(i, 0), ": ", names[i], newline))
OD
END
Each element is a separate string, and the loop prints an index next to each
name. The strings in this row are created by the denotation, so each one has
the bounds of its own denotation: Alice runs from 1 to 5,
Bob from 1 to 3, and Carol from 1 to 5.
$ a68g names.a68 1: Alice 2: Bob 3: Carol
Next steps
The strings article applies the same operations to rows of characters, and the flexible rows article explains rows whose bounds may change. The loops article describes the clauses used to walk a row, and procedures shows how the row parameters above are declared.
Source
Revised Report on the Algorithmic Language Algol 68
In this article we have declared rows with inferred and explicit bounds, read their bounds, walked through their elements, copied a slice, used a two-dimensional row, passed rows to procedures, and stored strings in a row. The flexible rows article continues with rows that change size.
Author
List all Algol 68 tutorials.