Algol 68 unions
last modified November 13, 2026
A united mode is a mode whose values may belong to one of several other modes. A variable of a united mode holds a value and a mark that says which part of the union it currently belongs to, and a clause reads the value only after naming the variant. This article describes the declaration, the clause that selects a variant, and the parts of the language that work with united values.
Declaring a united mode
UNION takes a list of modes and forms a new mode from them. A
value of one of those modes may be assigned to a variable of the union, and
CASE … IN … ESAC chooses a branch by the variant that the value
currently carries.
MODE NUMBER = UNION(INT, REAL);
BEGIN
NUMBER n := 42;
CASE n IN
(INT i): print(("integer ", whole(i, 0), newline)),
(REAL r): print(("real ", fixed(r, 0, 2), newline))
ESAC;
n := 2.5;
CASE n IN
(INT i): print(("integer ", whole(i, 0), newline)),
(REAL r): print(("real ", fixed(r, 0, 2), newline))
ESAC
END
The first assignment stores the integer 42 and the case clause takes the
INT branch, where the value is available under the new name
i. The second assignment stores a real, and the same clause text
now takes the other branch. The branch declarator gives the value a name of
the branch mode, so the body can use it as an ordinary integer or real.
$ a68g basic.a68 integer 42 real 2.50
Selecting a variant in a procedure
The case clause may be the whole body of a procedure, which keeps the selection in one place. The parameter has the united mode, and the call may pass a value of any of the alternatives.
MODE NUMBER = UNION(INT, REAL);
PROC show = (NUMBER n) VOID:
CASE n IN
(INT i): print(("int ", whole(i, 0), newline)),
(REAL r): print(("real ", fixed(r, 0, 2), newline))
ESAC;
BEGIN
show(1);
show(2.75);
show(3)
END
The calls pass an integer, a real, and another integer, and each argument is united with the parameter mode before the call. The procedure then reports which variant it received. A value of a mode that is not in the union would be rejected at compile time rather than converted.
$ a68g show.a68 int 1 real 2.75 int 3
CASE as an expression
A case clause may yield a value, in which case every branch must yield a value of one common mode. This makes the clause usable inside a formula, which is how a row of mixed numbers is summed.
MODE NUMBER = UNION(INT, REAL);
BEGIN
[1:4] NUMBER values := (1, 2.5, 3, 4.25);
REAL total := 0;
FOR i TO 4 DO
total +:= CASE values[i] IN (INT n): n, (REAL r): r ESAC
OD;
print((fixed(total, 0, 2), newline))
END
Each branch yields a real: the integer branch widens its value, and the real
branch yields it unchanged. The clause therefore has mode REAL and
may be added to the total. The four values sum to 10.75, and the integer
elements take part in the sum through the widening in the branch.
$ a68g row.a68 10.75
Unions of structured modes
The alternatives of a union may be structured modes, so one variable can hold a point or a name. A structure display cannot be united directly, because the display has no mode of its own; the value has to be stored in a variable of the structure mode first.
MODE POINT = STRUCT(INT x, INT y);
MODE SHAPE = UNION(POINT, STRING);
PROC describe = (SHAPE s) VOID:
CASE s IN
(POINT p): print(("point ", whole(x OF p, 0), " ", whole(y OF p, 0), newline)),
(STRING name): print(("name ", name, newline))
ESAC;
BEGIN
POINT p := (3, 4);
SHAPE s := p;
describe(s);
s := "circle";
describe(s)
END
The variable starts with the point and later holds a string, and the procedure
prints the fields of the point or the text of the string. The declaration
SHAPE s := (3, 4) would be rejected, because
(3, 4) is a display that needs the mode POINT from
its context and a union provides several contexts at once.
$ a68g shape.a68 point 3 4 name circle
A union field in a structure
Unions are often used as the mode of a field, which gives a record one slot that may hold different kinds of value. The selection of the field is followed by the case clause on that field.
MODE NUMBER = UNION(INT, REAL);
MODE ENTRY = STRUCT(STRING name, NUMBER value);
BEGIN
ENTRY e := ("pi", 3.14);
CASE value OF e IN
(INT i): print((name OF e, " integer ", whole(i, 0), newline)),
(REAL r): print((name OF e, " real ", fixed(r, 0, 2), newline))
ESAC
END
The entry holds the name pi and a real value, so the
REAL branch runs and prints the value with two decimals. The
name field is read inside the branch, because the case clause
only constrains the union field.
$ a68g entry.a68 pi real 3.14
The OUT branch
When the case clause has an OUT part, that part runs if no branch
matches the variant. It is written after the last branch, without a
declarator, and closes with ESAC as usual.
MODE NUMBER = UNION(INT, REAL);
BEGIN
NUMBER n := 3.5;
CASE n IN
(INT i): print(("int", newline))
OUT
print(("other", newline))
ESAC
END
The clause has a branch for integers only, and the value is a real, so the
OUT part prints the fallback line. The compiler accepts the
clause even though one alternative of the union has no branch, which makes
OUT the place for the cases that the branches do not cover.
$ a68g out.a68 other
When no branch matches
Without OUT, a value whose variant has no branch leaves the case
clause with no work to do. In a context that expects a value this is a
mistake, but as a statement the clause simply does nothing and the program
continues.
MODE NUMBER = UNION(INT, REAL);
BEGIN
NUMBER n := 2.5;
CASE n IN
(INT i): print(("int", newline))
ESAC;
print(("after", newline))
END
The real value matches no branch, so the clause prints nothing and the next
statement prints the message. Leaving out OUT is therefore safe
only when every alternative is covered or when doing nothing is intended.
$ a68g nomatch.a68 after
Testing identity with IS
Algol 68 Genie predefines IS and ISNT as tests of
reference identity: the result is true when both operands designate the same
variable. They do not test which variant a union carries, because that is the
work of the case clause.
BEGIN INT x := 5; REF INT a = x; REF INT b = x; REF INT c = LOC INT := 5; print((a IS b, " ", a IS c, newline)); print((a ISNT b, " ", a ISNT c, newline)) END
The names a and b both designate x, so
a IS b is true, while c designates a separate cell
of the same value and the test is false. ISNT is the negation of
the same test. Using IS to ask whether a value is an integer, as
in n IS INT, is a syntax error in this implementation.
$ a68g identity.a68 T F F T
Next steps
The structures article describes the modes
that most often appear inside a union, and the
conditionals article explains the
CASE clause on integers that shares its syntax with the case
clause on unions. The coercions article
covers the uniting coercion that makes show(1) acceptable, and
references and the heap explains reference
identity in more detail.
Source
Revised Report on the Algorithmic Language Algol 68
In this article we have declared united modes, selected a variant with
CASE … IN … ESAC, used the clause in a procedure and as an
expression, stored structures and strings in one variable, put a union in a
structure field, added an OUT branch, seen what happens when no
branch matches, and tested reference identity with IS and
ISNT. The references and the heap
article continues with the storage that such values live in.
Author
List all Algol 68 tutorials.