_ __ ___
/ \ / /_ ( _ )
/ _ \| '_ \ / _ \ On Algol 68 multiples, and ghosts
/ ___ \ (_) | (_) |
/_/ \_\___/ \___/
Jose E. Marchesi
January 1, 2026
This article provides a somewhat detailed introduction to Algol 68
"multiples", the data structures that roughly correspond to what are
commonly known as "arrays" in other programming languages, but that
differ from these in several peculiar and interesting aspects. It
then also discusses some aspects on how better implement them, and the
realization in the GNU Algol 68 compiler based in GCC.
Rows and multiples
We will start by establising a clear separation of two different
concepts that are sometimes muddled in other programming languages:
that of the type of a value and the kind of a value itself.
Data types are called "modes" in Algol 68. Each mode defines a set of
static properties that apply to values which are "acceptable" to that
mode. An integral value 12, for example, is acceptable to the mode
'int'. In other programming languages we would say that 12 is a value
of type int.
As such, modes only exist at compile-time [1], and guide the compiler
to generate code such as the static properties of the values
acceptable to these modes are always preserved. The particular values
acceptable to that mode, created and manipulated by the program, on
the other hand, are internal objects and exist at run-time.
Formal declarers for row modes:
[]int { row of integers }
[][]int { row of row of integers }
[,]int { row row of integers }
The bounds of multiples are dynamic up to the point the value gets
generated. Once the multiple comes to existence, its bounds stay the
same and cannot be changed. What is more, a name that refers to a
multiple can only be made to refer to another multiple having exactly
the same bounds.
For example, consider the following name that refers to a multiple of
three reals yielded by a sample generator:
[3]real vector;
At the point the multiple gets generated, it contains three undefined
reals. In the case of GCC, these reals are all 0.0. We can now use
an assignation to supercede the multiple referred by that name. For
example:
vector := (1,0,1);
Assigning a multiple value involves checking the bounds of the value
being assigned, to make sure they match exactly with the bounds of the
value being superceded. In this case, the bounds of the assigned
value are '1:3', which match with the bounds of the original
'(0.0,0.0,0.0)'. The assignation is valid and is carrief forward.
Thereafter the name 'vector' will refer to (a copy of) the value
'(1.0,0.0,1.0)'.
Had we written instead:
vector := (1,0);
We would then have triggered a run-time error complaining about the
disparity of bounds: you cannot make the name to refer to a multiple
of two reals. Since assignation is the only way provided by the
language to supercede the value referred by a name, this check is
enough to guarantee that once a name is made to refer to some given
multiple, it will always refer to multiples having the same number of
elements.
This explains why the bounds of a multiple are not a property part of
the mode they are acceptable to, but only of the particular multiple
value. This is not to be confused with the rank of the multiple,
which is in fact a static property of the row mode and therefore
checked at compile-time.
Footnotes:
[1] United modes, whose values include an overhead that identify the
mode of the value currently stored, refer to modes but only indirectly
and always relative to the acceptable modes, so this statement still
holds true.