Full form | brief form | in 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) |
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;}”.
Type Expression | informal meaning | OCaml |
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.
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.
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.