The 1975 report, some links, Synopsis once at http://www.seasys.demon.co.uk/SSP82h/Algol68.html and resurrected from wayBack machine. Some of my Algol 68 Programs

An Algol 68 Interpreter

There is a very nice little (!!) program that interprets Algol 68 programs from the Darwin shell of Mac OS X. It comes from Marcel van der Veer from whose page you can find the interpreter. Aside from Algol 68 being a very civilized language, this particular platform provides arbitrary floating precision! It must be compiled but it compiled and ran absolutely the first time for me! (Details and short cut for the Mac.)

Pieces of Algol68

You can’t understand a language from a smattering of ideas from the language but Algol68 had many ideas that are interesting apart from the rest of language. A good many showed up in other languages but many have not. The book “Informal Introduction to Algol68” by C. H. Lindsey is a detailed description of the language. ISBN = 0720407265. The official definition (too) is infamously daunting—it invents a language to describe the language.

Strophing

Following Algol60 the language calls for two sorts of identifier which are to be distinguished in an implementation dependent way. Published documents in and about the language use bold face for the distinguished sort of identifier with built in meanings. I will do the same here. Before computer IO devices understood lower case letters the most common practice was to precede the distinguished identifiers with an apostrophe. Van der Veer’s system referred to above presumes that a string of upper case letters is a distinguished identifier. We configured our compiler from Cambridge to interpret identifiers whose first character was upper case, as distinguished.

Brief and Full Forms of constructs

This aspect of the language may have been a compromise between the language designers but it has its charms. For several of the constructs there are the brief and full forms. The meanings of the two forms are identical but the brief form lacks English words which might convey meaning to the uninitiated.

Full formbrief formin C
if a < b then x(a) else y(b) fi (a < b | x(a) | y(b)) if(a < b) x(a); else y(b); or a<b?x(a):y(b)
if a < b then x(a) fi (a < b | x(a)) if(a < b) x(a);
if b then x elif b2 then x2 fi(b | x |: b2 | x2)if(b) x; else if(b2) x2;
case n in exp, exp, exp out exp esac(n|exp, exp, exp | exp) switch (n) {1: exp; break; 2: exp; break; 3: exp; break; default: exp;}
case u in (int i): i+3, (proc (real)void f): f(3) out exp fi (u | (int i): i+3, (proc (real)void f): f(3) | exp)
In each of these constructions the members can be expressions or statements. The C construct a?b:c is subsumed as (a|b|c). The last case statement is the type discriminator for a value u whose type is a union of several other types. The brief forms look ambiguous but the type of the “(e|” part determines which sort is meant. Algol68 has type safe unions as the semantics requires that the implementation provide an invisible type tag field.

Whereas semicolons terminate statements in most languages, they separate statements in Algol68. Thus: “if a>3 then a -:= 1; n +:= 2 fi” or “(a>3|a-:=1; n+:=2)” which in C would be “if(a>3) {--a; n+=2;}”.

Types

Many new ideas on types first found their way into real languages with Algol68, I think. Some have survived in other languages and others have not. The report uses “mode” where most use “type”. Here I use “type”. Every value is of just one type. There are the conventional base types, int, real, bool, char, void. There are four ways of constructing types from other types:
ref <type>, []<type>, struct(<type> n1, <type> n2, <type> n3), (<type>, <type>)<type>.
Type Expressioninformal meaningOCaml
ref <type>Name of, or mutable reference to a <type><type> ref
[]<type>Array of <type>s<type> array
struct(<type> n1, <type> n2, <type> n3) record with three named fields{ n1 : <type>; n2 : <type>; n3 <type> }
(<type>, <type>)<type_R> function of two args, returning value of type <type_R> <type> -> <type> -> <type_R>

ref real is a pointer to a floating point number. (Well actually see this about that.) []char is an array of characters and thus a string. The struct is much like C. (int, bool)real: is a routine that takes an integer and a boolean as arguments and returns a float. Routines are first class values with lexical scoping but the language does not require that routines persist beyond the stack frames that they depend on. A few implementations provided such persistence however. These type combination rules are highly orthogonal—about the only exception that I recall is that a structure cannot have a void field and I don’t understand that restriction. []ref int and ref[]int are distinct useful types.

Unions of types are also possible but the result is a “mood” and not a type (mode). If this were not so then we could not say that each value, such as 3, is of exactly one type, int. It would have been also of type union(int, real). The latter, however is a mood, not a type. References, parameters and structure fields can be of either some type or some mood. Unions are associative — union(int, union(char, bool)) is the same mood as union(char, union(int, bool)). Moods can be used by the type constructors. Moods are a bit like the ‘classes’ of von Neumann-Bernays set theory.

Types can be recursive but either a ref or a proc must intervene in the recursive loop.

Assignment

The assignment statement x := y evaluates both sides of the statement. The only rule is that for some type zot the type of x must be ref zot and y must be coerced to type zot.

Operators are overloaded as in Fortran where “+” can take either real or int operands and yield a value of matching type. New operators could be introduced such as “%*” and their precedence stated. New overloading of old operators can be introduced. More. The operand position of operators is a weak context whereas arguments to functions is strong.

Koans

MODE A = [~]INT; 
PROC f = (INT j)A: ([j]INT w; FOR k TO j DO w[k] := k OD; w);
A r = f(5); print(r)
⇒ 1 2 3 4 5

The first line defines “A” as the type of arrays of integers. The second line defines f as a function that takes an integer and returns such an array. In the function body, “[j]INT w” creates a mutable array of j integers and binds “w” to that array. Next we mutate the array and define all its elements. Next we return, not the mutable array, but the immutable array of type “A”. Naïvely the array w is copied into array r. Some compilers will do this with only one array. The third line calls f to get an immutable array “r” and prints that. Few languages can even define an immutable array. Most that can, can not create them so simply and usefully.