Blog › ICP guides

ALGOL developer on retainer: call-by-name parameter semantics, Jensen’s device, ALGOL 60 block structure, and ALGOL on monthly retainer

October 2, 2026 · ~13 min read

An ALGOL developer was maintaining a legacy data processing program at a geophysical research institute. The program accumulated sensor readings into a results array using a procedure called from a FOR loop. The procedure, record_reading, accepted three formal parameters: the value to store, the current position index, and the accumulator. It was written expecting that modifying the position index inside the procedure would advance an internal pointer without affecting the loop counter in the calling code. Instead, after each call, the loop counter had already advanced by one extra step.

The procedure was declared as:

procedure record_reading(val, pos, accum); real val, accum; integer pos;

Inside the procedure body, pos := pos + 1 was used to advance to the next storage slot. In ALGOL 60, formal parameters not declared with value are passed by name: every access to pos inside the procedure re-evaluates the actual argument at the call site. The calling code was for k := 1 step 1 until reading_count do record_reading(sensor[k], k, cumulative[k]). The formal pos was the actual variable k. When the procedure executed pos := pos + 1, it incremented k through the by-name binding. The FOR loop’s own step increment then added 1 more. On each iteration, k advanced by 2. Of 10 expected readings, only 5 were processed (indices 1, 3, 5, 7, 9). Wrong readings processed: 5 → 0 after adding value pos; to the procedure formal parameter list.

ALGOL overview: the ancestor of modern procedural languages (1958–1968)

ALGOL (Algorithmic Language) began with the IAL (International Algorithmic Language) report of 1958, produced by a joint committee of American and European computer scientists. The ALGOL 60 report, edited by Peter Naur and published in 1960, introduced the notation that became the standard way to define programming language syntax: Backus-Naur Form, named for John Backus who contributed the original meta-language and Peter Naur who refined it for the ALGOL 60 report. The ALGOL 60 design embodied recursive block structure, lexical scoping, pass-by-name parameter passing, and free-format source layout — all novel at a time when IBM FORTRAN had fixed-field card columns and no block structure at all.

ALGOL 60 never became a commercial product in the way that FORTRAN and COBOL did, but it became the lingua franca of algorithm publication. The ACM and European journals published algorithms in ALGOL 60 notation from the 1960s through the 1980s. The languages that followed — CPL (1963), BCPL (1967), C (1972), Pascal (1970), Simula (1967, the first object-oriented language), and Modula (1978) — all derived directly from ALGOL’s block structure and scope rules. ALGOL 60 is the common ancestor of most modern procedural and object-oriented languages; a C developer reading ALGOL 60 today would recognize the block structure, the procedure declarations, and the expression syntax immediately.

ALGOL 68 (1968), designed by Adriaan van Wijngaarden and the IFIP working group, was a radical redesign with a completely orthogonal type system based on modes (the ALGOL 68 term for types). Any mode could be dereferenced, procedured, arrayed, or structured; the mode system was self-describing and generated from a two-level grammar. ALGOL 68 was considered too complex for practical implementation at first but was eventually implemented in ALGOL 68C, ALGOL 68S, and later ALGOL 68G (the current open-source interpreter). ALGOL 68 influenced the design of Ada and, through the REVISED REPORT style of formal specification, the design of Standard ML.

ALGOL retainer work today exists at computation centers, national laboratories, and research institutes that still run numerical analysis programs originally written in the 1960s and 1970s. These programs process geophysical data, statistical calculations, and engineering simulations. The programs work; the organizations that depend on them have no incentive to replace them; and the maintenance burden falls on consultants who understand both the ALGOL language semantics and the numerical domain. ALGOL 68G provides a current runtime for ALGOL 68 programs; MARST provides a mechanical translator from ALGOL 60 to C for programs that need to run on modern platforms.

Call-by-name vs call-by-value parameter passing in ALGOL 60

ALGOL 60 defines two parameter passing mechanisms. Call-by-value is declared by listing the formal parameter in a value declaration at the top of the procedure body: value n, m;. At the call site, the actual argument is evaluated once and its value is copied into the formal parameter. Modifications to the formal inside the procedure do not affect the actual. Call-by-name is the default for all formal parameters not declared value. With call-by-name, the formal parameter is not a copy — it is a thunk: a deferred evaluation of the actual argument expression that fires at every access. Each time the formal is read or written inside the procedure, the actual argument expression is re-evaluated in the calling scope.

The practical consequence: when the actual argument is a simple variable (not an expression with side effects), reading the formal reads the current value of that variable, and writing the formal writes to that variable in the calling scope. This makes call-by-name a natural mechanism for output parameters: a procedure that writes a result back to the caller can accept a variable by name and assign to the formal. No pointer, no return value — the assignment is direct. When the actual argument is an array subscript expression like data[i], reading the formal re-evaluates data[i] using the current value of i at each access. If i changes between two accesses to the same formal, the formal may read or write different array elements.

The aliasing bug occurs when two formal parameters share a common variable in their actual argument expressions. Suppose the calling code passes k as one formal and data[k] as another. If the procedure modifies the first formal (k), the subscript in the second formal changes. The first write to the second formal stores at position k; after the modification, the second formal re-evaluates to data[k+1]. The assignment that the developer intended to store at position k has stored at position k+1. The array element at position k is never written. No error is raised. The only indication is wrong output values. Diagnosis requires tracing every formal parameter that is accessed multiple times or that has another formal sharing a common variable in its actual argument expression.

The fix for unintended aliasing is always the same: add value pos; (or the affected formal name) to the value declaration list at the top of the procedure body. This converts the parameter to call-by-value: the actual is evaluated once at the call site, the result is copied into the formal, and no further connection exists between the formal and the actual. All modifications to the formal inside the procedure are local. The calling scope is unchanged. For intentional output parameters — where the assignment to the formal is the desired side effect — call-by-name is correct and must be preserved. The retainer developer must distinguish intentional from unintentional call-by-name use, which requires reading the procedure’s intended behavior, not just its code.

Jensen’s device, own variables, and ALGOL 68 strong typing

Jensen’s device is the deliberate and correct use of call-by-name to implement parameterized summation. The classic example: a procedure sigma(expression, index, lower, upper) computes the sum of expression as index ranges from lower to upper. With value lower, upper; but no value expression, index;, the formal expression re-evaluates at each step of the loop inside the procedure. If called as sigma(a[j], j, 1, N), the formal expression evaluates to a[j], and the formal index (bound to the actual j) is incremented by the procedure’s internal loop. The procedure computes the exact sum of a[1] through a[N] without the caller writing any loop. Jensen’s device is elegant and correct; it was cited by Dijkstra and Naur as a demonstration of the power of call-by-name semantics.

Own variables in ALGOL 60 are the equivalent of static local variables in C: declared inside a procedure with the own prefix, they retain their value between calls to the procedure. An own variable is initialized once when the program starts, not on each call. A common maintenance bug: a developer adds a counter to a frequently-called procedure using a regular local variable (declared inside the procedure block), expecting it to accumulate across calls. But non-own local variables in ALGOL 60 are initialized at each procedure entry — the counter is reset to zero on every call. The fix is to use own integer call_count := 0; instead. The distinction between own and non-own local variables is invisible in the syntax of the procedure call; the only way to detect the bug is to recognize that the counter never exceeds 1.

ALGOL 68’s mode system introduced a form of type safety that was unprecedented in 1968. Every value in ALGOL 68 has a mode (its type). Modes can be composed: ref int is a reference to an integer (an lvalue); proc (int) real is a procedure taking an integer and returning a real; []int is an array of integers. Coercions transform values between related modes: dereferencing converts ref int to int; widening converts int to real; deproceduring calls a nullary procedure to produce its result. The mode checker enforces that coercions are applied correctly at every use site. ALGOL 68 retainer work often involves diagnosing coercion failures: a value at the wrong coercion level for its context, or a mode declaration that does not match the assigned value because the ALGOL 68 revision added a stricter mode. The work log entry for an ALGOL 68 coercion bug must name the mode, the coercion applied (or expected), and the context where the mismatch occurred.

Typical ALGOL retainer work and what it looks like in a work log

Call-by-name aliasing is the largest category of ALGOL retainer work that produces no visible artifact. A procedure with an unintended by-name formal has been running correctly for all calls where the actual argument does not share a variable with any other actual. Most calls are safe; the aliasing only manifests when the combination of actual arguments at a specific call site produces the alias. A program that processes 10 types of sensor readings and calls record_reading with independent variables for 8 of them runs correctly for 8 types and silently wrong for 2. Work log entry: “record_reading procedure: formal pos not declared value; passed by name as loop counter k; pos := pos + 1 inside procedure incremented k by name; FOR loop step then added 1 more; loop visited only odd indices 1,3,5,7,9; readings at even indices 2,4,6,8,10 skipped; fix: added value pos; to procedure; wrong readings processed: 5 → 0; 4h.”

Own-variable initialization errors are the second category. A developer expects a local counter or accumulator to persist between procedure calls but uses a regular local variable. The counter resets to zero on every call. The diagnosis: add tracing to observe the counter value at each entry. Work log entry: “compute_running_average procedure: integer call_count; declared as regular local variable; reset to 0 on each call; call_count := call_count + 1 always produced call_count = 1; running average always returned first-element value; fix: changed to own integer call_count := 0;; wrong average values before: 9 (all calls after first); after: 0; 3h.”

ALGOL 68 coercion failures are the third category. A mode change in a data structure declaration cascades to procedure call sites where the wrong coercion level is now applied. Work log entry: “process_record procedure: parameter mode changed from ref []real to []real in ALGOL 68G recompile; call site still passing a ref []real value; ALGOL 68G coercion context expected []real not ref []real; fix: added explicit dereferencing at call site; compiler error count before: 1; after: 0; 2h.”

Track ALGOL developer retainer hours without the status emails

When a 4-hour session diagnoses a call-by-name aliasing bug where the procedure modified the outer loop counter by name — because ALGOL 60’s default parameter passing re-evaluates the actual argument at every formal access, and the fix is a single value pos; declaration — the work log needs to name the procedure, the formal parameter, the actual argument, the aliasing mechanism, and the wrong-result count. HourTab gives your ALGOL retainer client a public dashboard URL they can bookmark: hours used, hours remaining, and a work log that names the ALGOL mechanism. No client login. No status emails. CSV in, URL out.

See HourTab pricing →

How HourTab tracks ALGOL developer retainer hours

ALGOL retainer work is invisible by the same mechanism that makes call-by-name semantics powerful: the aliasing only manifests at specific call sites with specific actual argument combinations. A program that has processed scientific data correctly for years may have a latent call-by-name aliasing bug that never fires until a new sensor type is added with a data array subscripted by the same variable used as the loop counter. The program runs normally for all previous data; it is silently wrong for the new data type. The connection between a missing value declaration in a 1972 procedure and wrong outputs in a 2026 data run requires understanding ALGOL 60 call-by-name semantics and the specific call site where the aliasing occurs.

The work log needs to name the mechanism: which procedure, which formal parameter, what the actual argument was, how the aliasing connected the formal to an unintended variable, what the observable symptom was (wrong index, wrong array element, wrong loop count), and what the fix was. A log entry that says “fixed parameter bug, 4h” is not auditable. A log entry that says “record_reading: formal pos not declared value; call site passed loop counter k as pos; pos := pos + 1 inside procedure incremented k by name; FOR loop step incremented k again; loop skipped even indices; wrong readings processed: 5 → 0; value pos; fix; 4h” is auditable.

HourTab gives ALGOL developers a public retainer-hours URL they send to clients — computation centers maintaining geophysical simulation programs, research institutes running statistical analysis code, and universities maintaining numerical methods libraries written in the 1960s through the 1980s. For ALGOL retainers, each work log entry should name the ALGOL mechanism: which procedure, which parameter passing mode, which aliasing path. Comparative context: ALGOL retainer work has conceptual overlap with other environments where parameter passing semantics require explicit attention — Fortran (where assumed-shape array bounds and INTENT declarations have analogous subtleties); Ada (where in/out/in out parameter modes make pass-by-reference explicit); and COBOL (where WORKING-STORAGE vs LINKAGE SECTION variable scope has analogous visibility bugs). ALGOL is uniquely positioned as the language that defined modern block structure and whose call-by-name semantics underpin both correct use (Jensen’s device) and the most subtle bugs in legacy scientific software.

FAQ: ALGOL developer retainers

What does an ALGOL developer on retainer typically do?

An ALGOL developer on monthly retainer covers call-by-name parameter semantics diagnosis (identifying where formal parameter assignments modify actual variables at the call site; adding value declarations to prevent unintended side effects; fixing array subscript aliasing bugs where two formal parameters alias through a shared subscript variable); block structure scope maintenance (nested begin/end blocks and own variable declarations; correcting variable declarations placed at the wrong block level); procedure and function maintenance (ALGOL 60 procedures return values by assignment to the procedure name; ALGOL 68 operator overloading and mode declarations; own variables for static state); ALGOL 68 strong type system maintenance (mode declarations, conformity clauses, coercion levels, rowing and deproceduring); and compiler toolchain maintenance for ALGOL 68G, MARST (ALGOL 60-to-C translator), and academic ALGOL 60 translators.

What ALGOL work is most commonly underlogged?

Call-by-name parameter aliasing diagnosis is the most underlogged: a formal parameter not declared value is passed by name; every access inside the procedure re-evaluates the actual argument; when two formals share a common variable through their actuals (e.g., k and data[k]), modifying one formal changes what the other refers to on the next evaluation; the aliasing produces wrong array writes invisible until results are inspected; fix is adding value to the affected formal; 3 to 6 hours. Own-variable initialization errors: a regular local variable resets to zero on every call instead of persisting; fix is own prefix; 2 to 4 hours. ALGOL 68 coercion failures: a mode change cascades to call sites where the wrong coercion level is now applied; fix is explicit dereferencing or mode adjustment at the call site; 2 to 5 hours.

What are typical ALGOL developer retainer rates?

Entry-level ALGOL developers with 1 to 2 years covering ALGOL 60 block structure, basic procedure maintenance, and call-by-name debugging typically bill at $65 to $120 per hour. Mid-level ALGOL programmers with 2 to 4 years covering call-by-name aliasing diagnosis, ALGOL 68 mode system maintenance, and scientific computing programs typically bill at $100 to $175 per hour. Senior ALGOL developers with 4 or more years covering Jensen’s device maintenance, ALGOL 68 type system interactions, and ALGOL-to-C migration planning typically bill at $145 to $265 per hour. Monthly retainer ranges: $800 to $1,800 per month for advisory engagements (8 to 15 hours per month); $1,800 to $5,000 per month for active program maintenance and migration work.

What should an ALGOL developer retainer agreement include?

A retainer agreement should specify: ALGOL version scope (ALGOL 60, ALGOL W, ALGOL 68 — each has different parameter passing semantics, type systems, and toolchains; ALGOL 60 has call-by-name as the default; ALGOL 68 has a strong orthogonal mode system with coercions); compiler and toolchain scope (ALGOL 68G for ALGOL 68; MARST for ALGOL 60-to-C translation; each has different diagnostic capabilities); parameter passing scope (whether call-by-name aliasing analysis is in scope; aliasing can affect any procedure in the program, not just recently modified code); migration scope (whether ALGOL-to-modern-language translation is in scope; migration requires identifying every by-name parameter with intentional side effects before converting to by-value semantics); and hour logging format (the procedure name, the formal parameter and its value/name status, the actual argument, the aliasing mechanism, the fix applied, and the wrong-result count before and after).

How should ALGOL developer retainer hours be logged?

Log each ALGOL retainer session with: the procedure name where the parameter semantics bug was diagnosed (e.g., record_reading); the formal parameter and whether it was declared value (e.g., pos not declared value, passed by name); the actual argument at the call site (e.g., loop counter k passed as pos); the aliasing mechanism (e.g., pos := pos + 1 inside procedure incremented outer k by name; FOR loop step incremented k again; loop skipped even indices); the fix applied (e.g., added value pos; to formal parameter list); and the wrong-result count before and after. For own-variable bugs: the variable declaration, the expected persistence behavior, the reset behavior observed, and the fix. For ALGOL 68 coercion bugs: the mode involved, the coercion context, the mismatch, and the fix.