[RFC,V2] GNU Algol 68 Coding Guidelines

Jose E. Marchesi jemarch@gnu.org
Mon Jan 12 19:27:55 GMT 2026


New version, with some typos and some additional contents added based on
the feedback received so far..

---
    _    __    ___
   / \  / /_  ( _ )
  / _ \| '_ \ / _ \           GNU Algol 68 Coding Guidelines
 / ___ \ (_) | (_) |
/_/   \_\___/ \___/




                                                      Jose E. Marchesi
                                                       January X, 2026


          Please send suggestions and proposals related to
          this document to algol68@gcc.gnu.org


This document contains a set of conventions and recommendations for
writing Algol 68 code which is part of ga68, the GNU Algol 68
compiler. As with any coding conventions, the goal is to achieve a
coherent style among the codebase.

Algol 68 is probably the programming language with the most carefully
and lovingly designed syntax ever made.  It can be verbose when it is
convenient for the programmer, and also extremely compact while
keeping an astounding high level of readability.  These conventions
aim to make a good use of that.

Feel free to adopt our guidelines for your own Algol 68 project,
partially or entirely.

In what follows we make extensive use of Algol 68 terminology, which
may be confusing at first for the uninitiated reader.  The Algol 68
Jargon File provides definitions for many of the terms used in the
context of the Algol 68 programming language and associated
technologies.  If you find yourself wondering about "frobyts" or
"enclosed clauses", please look them up in the jargon file.  The file
is available online in the www and, in Gentoo, as manpages once you
install the app-doc/a68-jargon package. Just type `man 7algol frobyt'
in the terminal, for example.

=> https://jemarch.net/a68-jargon The Algol 68 Jargon File

Additionally, in this document we use the term "space" and "spaces" to
refer to "typographical display features", i.e. spaces, tabs and
newlines.  These characters are of no significance and do not alter
the meaning of the program when they appear between symbols, outside
of string and character denotations, but they have a great impact on
the readability of the code and are the main tool for formatting.

## Stropping

The GNU Algol 68 compiler supports two stropping regimes:

* The "classic" UPPER stropping, which is one of the standard
  stropping regimes defined in the Standard Hardware Representation
  for Algol 68.  This regime uses upper-case letters to encode bold
  letters and lower-case letters to encode non-bold letters.

* The "modern" SUPPER stropping, which is a GNU extension.  This is
  the standard stropping regime in GCC, and its rules are similar to
  the naming conventions widely used in many modern programming
  languages.  The resulting programs have a very modern feeling.

In GCC we use SUPPER stropping only.  The only instance of UPPER
stropping are in test cases.  Some of the guidelines and
considerations in this document may also be useful in programs using
UPPER stropping.

## Formatting

The placement of spaces and empty lines in the program text plays an
important role when it comes to readability.

### Empty lines

Empty lines are often used in programs to separate logical parts in a
sequence of statements or expressions.  This avoids the code to look
like walls of text, which are somewhat difficult to read.  This of
course also applies to Algol 68, but in a much less degree due to the
exceptionally clean syntax of the language.  Therefore we favor a
compact formatting to a reasonable extent.

Please be frugal with empty lines, especially within enclosed clauses.

It is not necessary nor advisable to have separated "declaration
parts" in serial clauses, because declarations can appear anywhere.
However empty lines may still be useful to group related declarations
together.

It is often better to use an explanatory comment rather than an empty
line, again especially within enclosed clauses.

### Spaces before parentheses

Do not put spaces before open-parentheses in routine calls.

But make sure to always put a space between `union' or `struct' and
the open parenthesis that follows in declarers.

Likewise, please put a space before the open parenthesis when the
enclosed clause in a cast is a closed clause.

Examples:

```
{ No space before open-parentheses in calls }
puts(fixed(count,0) + "'n");

{ Space between `union' or `struct' and `(' }
mode Number = union (int,long int,real,long real)

{ Space before '(' in casts }
ref JSON_Fld (fields) := field;
```

### Spaces after parentheses

When writing routine texts always place a space between the formal
parameters pack and the mode of the value yielded by the routine.

When writing operator and procedure declarators do not put a space
between the parameter modes pack and the mode of the yielded value.

Also in declarers, do not put a space after `op' or `proc' and the
parameter modes pack.

```
{ Space after formal parameters pack in routine text }
json_foreach_elem(a, (ref JSON_Val v) void: len +:= 1)

{ No space after parameters pack in procedure and operator
  declarators }
proc(string)void error;
```

### Spaces within packs

With "pack" we refer to the following source constructs which are
collections of other constructs enclosed between `(' and `)' symbols:

* The actual parameters in a call.
* The formal parameters in a routine text.
* The fields in a struct mode declarator.
* The modes of the united modes in an union mode declarator.
* The modes of the parameters in an operator or procedure declarator.

Spaces are optional after commas in packs when both the preceding and
following symbols are tags.

Put a space after commas in packs when the next construct is not a
tag, but only if the preceding construct is a tag.

Do not put spaces before commas in packs.

Examples:

```
{ Spaces are optional around commas surrounded by tags }
process(socket, resp, fragmented);
process(socket,resp,fragmented);
op E = (Symbol a,b) bool: a = b;
op E = (Symbol a, b) bool: a = b;

{ Space after commas separating a non tag and a tag }
op E = (Symbol s, Word w) bool: s E w;
mode M = struct (int i, real r);

{ No spaces in commas separating non tags }
proc(int,string,[]real)int callback;
op(intint)int handler;
mode Data = union (void,bool,int)
```

### Spaces in row displays

Within row displays spaces are optional after commas, but please never
put spaces before commas.

```
{ Spaces are ok after commas in row-displays }
[]int a = (1,2,3);
[]int b = (1, 2, 3);
[]string names = ("jemarch",
                  "mnabipoor",
                  "pietr0");
```

### Spaces in formulas

Do not put spaces after monadic operators whose representation is not
a bold word.

However, if the monadic operator is represented by a bold word, always
put a space between the operator and the operand, even when the
operand starts with a parenthesis.

Always put spaces before and after dyadic operators if the formula is
not parenthesized.  Spaces are optional if the formula is
parenthesized, provided the operator is not represented by a bold
word.

Examples:

```
{ No space after non-bold monadic operators }
int i = -10;

{ Always a space after bold monadic operator }
int i = ABS (base + offset)

{ Spaces in dyadic opeator }
total := a + b;
index := cnt +:= 1;
total := (a + b);
index := (cnt +:= 1);
total := (a+b);
index := (cnt+:=1)
```

### Spaces in bounds

Do not put spaces after the bounds of a declarer.

Also, do not put spaces directly within the bounds of a declarer,
unless for indentation purposes.  Since bounds can contain any unit,
the general rules apply within these.

Examples:


```
{ No spaces after bounds in declarers }
mode List = [10]int,
     MatrixList = [10][3,3]int,
     Numbers = []union (int,real);

{ No spaces directly within bounds in declarers }
mode MyString = [1:10@]MyChar,
     DynamicTable = [read_int(10, 20),
                     read_int(10, 20)]char;
```

### Spaces in indexers and trimmers

Algol 68 allows using `(' and ')' instead of '[' and ']' in bounds and
slices to represent the SUB and BUS symbols. This is supported by ga68
via the `-fbrackets' command-line option in order to ease the porting
of old code, and it is disabled by default. Please always use square
brackets for indexing in new code.

While indexing and trimming a multiple, never put a space between the
indexed tertiary and the SUB symbol.

Do not put spaces direcly within indexers and trimmers, unless for
indentation purposes.  As an exception to this rule, you can put a
single space before the "at" operator `@' if desired.

Examples:

```
{ No space between tertiary and '[' }
int i = a[i];
int i = a[10:20]

{ No direct spaces within trimmers and indexers, but before @ }
[]int a = b[2:5@10];
[]int c = d[10:20 @1];
```

### Spaces in brief clause forms

It is generally a good idea to have spaces around `|' and `|:' within
the brief forms of conditional clauses, case clauses and conformity
clauses.

When the brief forms are very short and the units are number
denotations, it may be more clear to not use spaces, especially when
the form is an operand in a formula.

Examples:

```
{ Space around | and |: in brief forms }
(v | (void): "empty", (bool b): (b | "true" | "false"))

{ No spaces may be more readable sometimes }
int n = 2 + (c>3|10|20);
```

## Comments

Use "foo" to refer to formal parameters when documenting procedures or
operators.

Use `whatever' to refer to any other source construct that is not a
formal parameter.

Examples:

```
int error_hash = 0;

{ Return a hash code for the string "s", or `error_hash' if the string
  is too long.  }

proc hash_string (string s)
```

## Syntactic Conventions

### Closed clauses

Algol 68 allows using `(' an `)' instead of `begin' and `end' to
delimit closed clauses.  In fact, parenthesized expression in other
programming languages are realized in Algol 68 with closed clauses, in
a very orthogonal way.  Both forms are useful and can generally be
used according to the programmer's taste.  However, this section
contains a few guidelines and recommendations on this regard.

Use parentheses for closed clauses that span in a single line,
regardless of the context.  Having `begin' and `end' symbols in the
same line looks weird and confusing.

As a general rule, always use parentheses in closed clauses that are
operands in a formula.  Exceptionally, using `begin' and `end' in
formula operands may be preferable if the operand contains many
declarations and units, and only if it spans for more than one line.
In this case, however, plese consider factoring the code in the
operand into a routine and replace it with a procedure call.

The preferred indentation for a closed clause whose contents span for
more than one line, and that uses `begin' and `end' symbols as
delimiters, is to indent the contents right at the right of the
`begin' symbol.  The `end' symbol shall then be placed in its own
line, with the same indentation level than the opening symbol.

If the closed clause contains empty lines, or if the line preceding
the closed clause is so long that it would "hide" the first line in
the closed clause, then it is ok to put the first unit or declaration
in the line after `begin'.  This usually happens when the closed
clause is the body of a long routine text.

The preferred indentation for a closed clause whose contents span for
more than one line, and that uses `(' and `)' symbols as delimiters,
is to indent the contents right at the right of the `(' symbol.  The
')' symbol finishing the closed clause shall not be placed in its own
line.

```
{ No `begin' and `end' in the same line }
int i = 2 + (3+4);
int i = 2 + (int i = random(); i % 10 );
bool test = case v in (string): (puts (s); true) out false esac;

{ Closed clauses as formula operands }
int i = 2 + (int cnt := 0;
             to UPB data[@1] do cnt +:= 1 od;
             cnt)
int j = 2 + begin int cnt := 0;
                  to UPB data[@1] do cnt +:= 1 od;
                  cnt
                  end

{ Closed clause with no empty lines }
begin int fd = fopen ("data', file_o_rdonly")
      puts ("first line: " + fgets (fd, 0));
      fclose (fd)
end;

{ Closed clause with no empty lines but with preceding
  line of similar length }
proc parse_number = int:
begin
      int num := 0;
      while num := num * 10 + ABS ch - ABS "0";
            isdigi(getachar)
      do ~ od;
      ungeachar(ch);
      num
end;

{ Closed clause with empty lines }
proc main_proc = int:
begin
      { Auxiliary procs }
      proc aux1 = int: ...;
      proc aux2 = int: ...;

      { Computation }
      aux1;
      aux2;

      { Result }
      aux1 + aux2
end

{ Indentation of closed clauses using `(' and `)' delimiters }
proc parse_number = int:
(int num := 0;
 while num := num * 10 + ABS ch - ABS "0";
       isdigi(getachar)
 do ~ od;
 ungeachar(ch);
 num)
```

### Conditional clauses

If a conditional clause is still clear and not of excessive length
when written on a single line, just do it.

Start the enquiry clause in the if-part of a conditional clause right
after the `if' symbol, not in the next line.

The serial clauses in the then- and if-parts of the conditional clause
shall be indented five positions right, which is the length of both
the `then' and `else' symbols plus one.

The first declaration or unit in the tehn- and if-parts shall be
placed in the same line than the `then' and `else' symbols,
respectively.

Place the `fi' closing symbol in its own line, with the same
indentation level than the matching `if'.  The exception to this rule
is when the conditional clause has no else-part and the then-part
spans for a single line that is not too long.  In that case, place the
`fi' in the same line than `then'.

Examples:

```
{ Very small conditional clause in a single line }
if idx < 0 then fatal("invalid idx") fi

{ Short conditional-clause with `fi' in the same line
  than `then' }
if argc /= 3
then puts("expected two arguments'n") fi

{ A conditional-clause that spans for several lines }
if a > 10
then puts("truncating");
     a := 10
fi
```

### Loop clauses

If a loop clause is small enough to fit in a single line without
occupying most of it, just do it.

If a loop clause spans to two lines, and the second line is not too
long, you can put `od' in the same line than `do'.

If a loop clause spans to several lines, please put the `do' symbol in
its own line, indented to the same level than the clause's frobyts.

Examples:

```
{ Very short loop-clause in a single line.  }
for a to argc do puts ("arg: " + argv[a]) od;

{ Short loop-clause with `od' in the same line than `do' }
for i from LWB a to UPB a
do total +:= a[i] od

{ A loop-clause that spans for several lines }
while NOT exit
do string cmd = get_command;
   process_command(cmd)
od
```

### Case and conformity clauses

Do not write a case or conformity clause in a single line, unless
you are using the brief form.  Unlike conditional and loop clauses,
these are difficult to read.

Please put the `in', `out' and `ease' symbols in their own lines, with
the same indentation level than the matching `case'.

Start the choices right after the `in' symbol, in the same line.  All
the choices may fit in a single line.  If they don't, please put each
choice in its own line.

```
{ Short case clause }
case i
in 100, 200, 300 out 0 esac;

{ Long case clause }
case i
in 100,
   200,
   300
ouse i % 100
in 100,
   200,
   300
esac;

{ Long conformity clause }
case v
in (void): "empty",
   (bool b): (b | "true" | "false"),
   (string s): s
esac
```

### Procedure and operator declarations

In procedure and operator declarations, if the body of a routine text
starts with 'begin', put it at the same indentation than the `pub',
`proc' or `op'.  Otherwise, indent it three spaces to the right
relative to the `pub' `proc' or `op'.

Examples:

```
{ Body of routine is a `begin'..`end' closed clause }
proc checked_div = (int a,b) int:
begin
      if b = 0 then fatal ("div by zero") fi;
      a % b
end;

{ Body of routine does not start with `begin' }
proc checked_div = (int a,b) int:
   (b = 0 | fatal ("div by zero"); skip | a % b);

{ Body of routine is not a closed clause }
proc checked_div = (int a,b) int:
   if b = 0
   then fatal ("div by zero"); skip
   else a % b
   fi;
```

### Contracted declarations

Please don' be shy to use contracted forms of declarations.  They can
make the program much more readable and they make it easier to add new
declarations, because they prevent writing the same text again and
again.

However, care should be taken when declarin operators and procedures.
In these cases, contracted declarations should only be used when
declaring very short, one or two lines long routines.  The last
routine in the list of joined declarations can be a bit longer.

Examples:

```
{ Contracted declarations lead to compact and very
  readable code }
int disconnected = 0, connected = 0, unknown = 2;
pub ref JSON_Val json_no_val = nil,
    ref JSON_Elm json_no_elm = nil;
    ref JSON_Fld json_no_fld = nil;

{ Use contracted declarations for short routines }
op + = (States ss, State s) States: MoreStates (heap States := ss, s),
   + = (Transitions ts, Transition t) Transitions:
         MoreTransitions (heap Transitions := ts, t)
```

### Brief clause forms

The obvious context where to use the brief forms of conditional, case
and conformity clauses is when these clauses appear as operands in
formulas.  They match well with parenthesized closed clauses.

It is also ok to use brief forms of clauses out of formulas,
especially inside case and conformiy clauses.  But please be careful,
as brief forms may be confused sometimes.

Examples:

```
{ Brief forms in formulas }
int res = (a=0 | fatal("div by zero"); skip | den/a);

{ Brief forms out of formulas }
for i to ELEMS str
do char newline = REPR 10, tab = REPR 9, c = str[i];
   (c = "\" | res +:= "\\"
    |: c = newline | res +:= "\n"
    |: c = tab | res +:= "\t"
    |  res +:= c)
od
```

## Naming

Unlike most other programming languages, which are not stropped, in
Algol 68 it is possible to have tags with the same name than reserved
words, by appending an underscore character to the tag. For example, a
tag `if' can be written as `if_'.  It is important to note that the
trailing underscore is not part of the tag: it is just a stropping
artifact. This is always better than contriving artificious synonyms
that are often confusing or too long.  A copying routine has arguments
"from" and "to"? Call them `from_' and `to_'. A struct mode has fields
"in" and "out"? Call them `in_' and `out_'.

Please use fully uper-case bold words for operator indicants.  This
makes it easier for text editors to highlight them in a different
style than mode indicants, and look more symmetrical in case of dyadic
operators.  For example, use `ABS' and not `Abs'.

## Programing style

This section contains some recommndation on the usage of the
facilities of the language.

### Writing routines

Use routines liberally!  Routines are cheap, very easy to write thanks
to the excellent Algol 68 syntax for routine texts, and first-class
citizens in the language.  They also have access to the lexical
environment.  So if you find yourself wanting to write a macro in
order to repeat some little calculation, just write a small procedure
or operator instead.

Choose identifiers that are expressive of meaning in order to clarify
both the intent of the procedure or operator and the code written that
uses it.

Consider using overloaded operators in preference to procedures with
united mode parameters to encourage users of the operator to create
new versions of the operator for different parameter types, rather
than leaving the users trying to figure out how to hack the united
mode definition.

The high level of orthogonaliy of Algol 68 combined with the
structural type equivalence and the nice compact syntax of declarers
makes mode names way less relevant than in many other programming
languages.  In particular if a routine takes a parameter that is an
united mode, and that particular united mode is either not used
anywhere else or very short, just write the declarer, you don't have
to name it first.

Please make good use of the lexical block structure of the programming
language: is is there to be used.  In little local auxiliary routines,
do not add arguments just to pass a value thas is in the environment,
unless the declaration of the later is very far away from the routine
text: make the routine access the value itself.

### Nihils

Never use `nil' directly in identity relations; it is very error
prone.  It is better to define "nihils" for all reference modes that
are likely to appear in one.

Examples:

```
mode Node = ...;
ref Node no_node = nil;
while n :/=: no_node do ... od
```


More information about the Algol68 mailing list