Blog › ICP guides

ALGOL developer on retainer: block structure, static scoping, call-by-name parameters, ALGOL 60 procedure design, and ALGOL 68 mode declarations on monthly retainer

November 4, 2026 · ~18 min read

An ALGOL 60 numerical integration program was producing five wrong integration results per computation run. The program used a compound statement to compute numerical integration, with a local variable declared in an outer block’s scope. The compound integration procedure was activated twice concurrently — two separate integration jobs ran over different ranges of the same integrand. Under ALGOL 60’s static scoping rules, both activations resolved their reference to the shared variable to the same outer-block declaration: the same memory location held the variable for both activations simultaneously. When the first activation wrote its running sum into the shared variable and then the second activation read from it, the second activation read a partially-accumulated value from the first activation rather than its own initial value. Five computation runs per session used ranges where the two activations’ writes interleaved, producing wrong integration results from the shared accumulator. The ALGOL developer on retainer diagnosed the static-scoping variable sharing: both activations needed their own independent accumulator, but ALGOL 60’s scoping rules gave them one. The fix restructured the procedure to pass the accumulator as a formal parameter across the block boundary — a parameter receives a fresh binding per activation — rather than relying on a shared outer-scope variable. Wrong integration results per computation run: 5 → 0.

The work log entry read “fixed integration accumulator sharing, 15h.” It names the symptom and the duration. It cannot explain to a client why ALGOL 60’s static scoping rules cause all references to a variable name within a compound statement to resolve to the same outer-block binding regardless of how many times the compound statement is activated (ALGOL 60 was one of the first languages to introduce lexical scoping as a formal language feature; the ALGOL 60 report’s block structure rules specify that variable declarations are resolved at their lexically enclosing block, not at each activation of a procedure; the scoping rules that were intended to eliminate dynamic-binding surprises from earlier languages introduced a different class of sharing bugs when the same procedure body was activated concurrently with shared outer-scope variables), why passing the accumulator as a formal parameter eliminates the sharing (each procedure activation receives a new parameter binding in its own activation record, separate from every other activation’s parameter binding, even for parameters pointing to the same outer-scope storage), why call-by-value vs call-by-name parameter binding mode is the key design decision in this restructuring (call-by-value copies the argument expression’s value into the parameter at call time, giving the procedure a private copy; call-by-name re-evaluates the argument expression at each use, maintaining a live alias into the calling environment), or why this class of bug is structurally different from a race condition in languages with explicit threading (ALGOL 60 programs with concurrent activations share variable bindings through static scoping, not through shared memory addresses, and the fix is scoping restructuring rather than synchronization). The 15 hours of static scoping analysis, outer-scope variable audit across all compound procedure activation sites, parameter-passing restructuring, and verification of independent accumulator bindings per activation are not visible in the diff beyond changed parameter declarations.

ALGOL 60 block structure and static scoping

ALGOL 60 was designed in 1960 by an international committee including John Backus, Friedrich Bauer, Peter Naur, and Klaus Samelson as a universal algorithmic notation for scientific computing. Its central contribution to programming language design was the block structure: a begin...end compound statement that introduces a new scope for local variable declarations. Variables declared inside a block are local to that block and shadow any outer-scope variable with the same name. When the block exits, its local variables are destroyed and the outer-scope bindings become visible again. This is lexical (static) scoping: the binding of every variable reference is determined by the lexical structure of the program text, specifically by which begin...end block most immediately contains the reference and also declares the variable. ALGOL 60’s block structure was directly adopted by Pascal, C, and virtually every subsequent programming language that uses lexical scoping.

A block in ALGOL 60: begin followed by declarations, followed by statements, followed by end. Declarations must come before statements within a block; this is the block-structure rule that distinguishes ALGOL 60 blocks from C compound statements (which allow mixed declarations and statements). Local variable types: integer, real, Boolean, array. Array declarations: real array a[1:n] declares a real-valued array with index range 1 to n (ALGOL 60 uses dynamic array bounds, evaluated when the block is entered; n can be a variable visible in the enclosing scope). Label declarations: label myLabel; declares a label that can be used as a go to target. Procedure declarations inside blocks: procedure name(params); value list; specifications; body; where value list names the call-by-value parameters (all others are call-by-name), specifications gives type declarations for all parameters, and body is the procedure body (a block or a single statement). The declaration of a procedure inside a block means the procedure has access to all variables in its lexically enclosing blocks, a feature that enables nested scoping but also the outer-scope variable sharing bug described above.

Static scoping in nested procedure activations: when a procedure declared inside a block is activated, it can read and write any variable declared in any lexically enclosing block. If that variable is the same one another concurrent activation of the same procedure is using, both activations see the same storage. The ALGOL 60 specification’s treatment of block structure is purely syntactic and static: it does not describe what happens when the same procedure body is activated multiple times concurrently, because ALGOL 60 was specified for sequential execution. In practice, implementations that ran ALGOL 60 programs with concurrent compound activations (using the parallel construct in ALGOL 68 or in ALGOL 60 extensions) faced the outer-scope sharing problem structurally. The retainer task of static scoping analysis: for each procedure declaration inside a block, identify every outer-scope variable the procedure body references; for each outer-scope variable reference, determine whether any other activation of the same or a different procedure can write to the same outer-scope variable concurrently; and restructure shared variable access to use formal parameters or local declarations inside the innermost procedure body.

The go to statement in ALGOL 60: go to label; transfers control to the statement with the given label. Labels can be declared in any enclosing block and used in a go to from an inner block, enabling non-local exits. A go to that targets a label in an outer block exits all intermediate blocks, destroying their local variables. ALGOL 60 introduced go to as a general control transfer mechanism, and Peter Naur’s ALGOL 60 report included both go to and structured iteration to give implementors flexibility. Dijkstra’s famous “Go To Statement Considered Harmful” letter (1968) was a direct response to ALGOL 60’s permissive use of go to, and led directly to the structured programming discipline that subsequent ALGOL-family languages (Pascal, Ada) enforced by restricting or eliminating go to. ALGOL developer retainer work on control flow frequently involves replacing go to-based loop exits with structured for/while equivalents, or diagnosing non-local go to targets that cross block boundaries in ways that destroy local variable bindings the program intended to preserve.

ALGOL 60 parameter binding modes: call-by-value and call-by-name

ALGOL 60 introduced two formal parameter binding modes that have no direct equivalent in most later languages: call-by-value and call-by-name. The binding mode of each parameter is specified in the procedure declaration’s value list: parameters named in the value list are call-by-value; all other parameters are call-by-name (the default). Call-by-value: when a procedure is activated with a call-by-value parameter, the argument expression is evaluated once at the call site, and the resulting value is copied into a new local variable in the procedure’s activation record. Mutations of the parameter inside the procedure do not affect the caller’s variable. This is the familiar semantics used by most modern languages for scalar parameters. Call-by-name: when a procedure is activated with a call-by-name parameter, no evaluation happens at the call site. Instead, the argument expression itself (as text) is associated with the parameter name. Every time the parameter is read or written inside the procedure, the argument expression is re-evaluated in the caller’s scope. If the argument expression is a variable name, reading the parameter reads the current value of the variable; writing to the parameter writes to the variable. If the argument expression is a subscripted array element (a[i]), reading the parameter re-evaluates both the array base a and the index i in the caller’s scope at the moment of each access — including any changes to i that have happened since the call.

Jensen’s device is the most celebrated use of call-by-name parameter semantics, named after Jørn Jensen who described it in the ALGOL 60 report’s appendix. The classic example: a summation procedure with call-by-name parameters for the summand expression and the index variable. real procedure sum(a, k, l, u); value l, u; real a; integer k, l, u; begin real s; s := 0; for k := l step 1 until u do s := s + a; sum := s end. When called as sum(v[i], i, 1, n), the parameter a is bound call-by-name to the expression v[i], and the parameter k is bound call-by-name to the variable i. Each iteration of the for loop assigns a new value to k, which through the call-by-name binding writes to the caller’s variable i. Each evaluation of a re-evaluates v[i] in the caller’s scope, reading the array element at the current value of i. The result: the procedure computes the sum of v[1] + v[2] + ... + v[n], stepping the caller’s index variable through the range 1 to n. By changing the first argument to v[i] * v[i], the same procedure computes the sum of squares. By changing it to a[i][j] with i fixed and j as the index, the procedure computes a row sum of a matrix. Jensen’s device uses call-by-name to achieve parameterized summation that would require higher-order functions (or lambda expressions) in a call-by-value language.

Call-by-name aliasing is the dangerous side of call-by-name semantics. If a procedure with call-by-name parameters x and y is called with the same variable as both arguments — proc(a, a) — then x and y are aliases: writing to x inside the procedure writes to the caller’s a, and reading y reads the same location. This produces unexpected coupling between parameters that the procedure author assumed were independent. A more subtle aliasing case: proc(a[i], i) where both x (bound to a[i]) and y (bound to i) are call-by-name. If the procedure body assigns to y (which writes to i), the subsequent read of x (which evaluates a[i]) uses the new value of i, not the original index at call time. This aliasing is the structural source of the six wrong array element reads per computation run in a typical retainer bug report: the procedure’s body assumed the array subscript was frozen at call time (call-by-value semantics), but the subscript is re-evaluated through the call-by-name binding after the index variable is mutated. The fix: mark the index parameter i as value i in the procedure declaration, copying the index at call time rather than re-evaluating it on each access.

ALGOL 60 structured programming: for loops, conditionals, and recursive procedures

ALGOL 60’s for statement is the language’s principal iteration construct. The general form: for variable := for_list do statement, where for_list is a comma-separated sequence of for-list elements. A for-list element can be: a single expression (execute the body once with the variable set to that value); a step...until range (e1 step e2 until e3 iterates with the variable starting at e1, incrementing by e2, and stopping when the variable exceeds e3 in the direction of e2); or a while clause (expression while condition sets the variable to the expression and repeats the body as long as the condition holds). The step value in a step...until range can be negative: for i := n step -1 until 1 do iterates backward from n to 1. The while clause is ALGOL 60’s general-purpose loop: for x := x + 1 while x < limit do body is a while loop updating x each iteration. Multiple for-list elements: for i := 1, 3, 5, 7 step 2 until n, n do body iterates over the explicit values 1, 3, 5, then steps by 2 from 7 to n, then executes once with n.

The if statement in ALGOL 60: if condition then statement1 else statement2. When the then-branch or else-branch needs to execute multiple statements, it must be enclosed in a begin...end block: if condition then begin statement1; statement2 end else statement3. The classic ALGOL 60 dangling-else ambiguity: in if c1 then if c2 then s1 else s2, the else associates with the inner if (the nearest unmatched then), which is the standard disambiguation rule later adopted by C. ALGOL 60 also has a conditional expression (not just a conditional statement): if condition then e1 else e2 used in an expression context evaluates to e1 if the condition is true, otherwise e2. This is the direct predecessor of C’s ternary operator condition ? e1 : e2. The Boolean operators: (logical and), (logical or), ¬ (logical not), (implication), (equivalence). The comparison operators: <, , =, , >, .

Recursive procedures in ALGOL 60: ALGOL 60 supports recursive procedure calls directly — a procedure body can call the procedure by name, and each activation gets its own local variable bindings in a new activation record on the call stack. This was a significant advance over FORTRAN (which did not support recursive procedures in its early versions) and established recursion as a first-class programming technique. Recursive factorial: integer procedure fact(n); value n; integer n; fact := if n ≤ 1 then 1 else n × fact(n-1). The procedure body uses an ALGOL 60 conditional expression to return 1 for the base case or the recursive product for the inductive case. Mutual recursion: two procedures A and B that call each other require ALGOL 60’s forward declaration mechanism: declare A before B’s body in the outer block, then declare B with A’s body in scope. The procedure type annotation in ALGOL 60: procedure as a formal parameter type specifies that a parameter is itself a procedure, enabling higher-order programming without call-by-name (the procedure reference is passed as a value, and the called procedure invokes it through the parameter name).

ALGOL 68: modes, transput, unions, and parallelism

ALGOL 68 is a major revision of ALGOL 60, published in 1968, designed by Adriaan van Wijngaarden and the IFIP Working Group 2.1. ALGOL 68 is a more expressive language than ALGOL 60, introducing a general type system (called “modes” in ALGOL 68 terminology), orthogonal language design (every value has a mode; every mode can be used in every context), and powerful abstraction mechanisms. The mode system: mode MODENAME = mode_definition declares a new mode (type alias or type definition). Structural modes: STRUCT(REAL x, REAL y) is a structure mode with two real fields; [1:n] REAL is an array mode with bounds 1 to n; REF REAL is a reference-to-real mode (a pointer to a real value). Mode aliases: mode VECTOR = [1:N] REAL declares VECTOR as an alias for a fixed-size real array; mode MATRIX = [1:M, 1:N] REAL declares a two-dimensional real matrix type. Union modes: union(INT, REAL, STRING) is a mode whose values can be any of the listed constituent modes; a union value carries a tag indicating which constituent type it holds. The case clause deconstructs a union value: case v in (INT n): ..., (REAL r): ..., (STRING s): ... esac binds the appropriate variable and executes the matching arm.

ALGOL 68 transput (I/O): the transput system uses books, channels, and files. A channel is an abstract I/O endpoint (standard input, standard output, a file on disk). A book is opened from a channel and provides the actual read/write operations. The standard transput procedures: read(x) reads a value from the standard input channel into variable x; write(x) writes the value of expression x to the standard output channel; print(x) is equivalent to write(newline; x). File transput: open(f, "filename", stand in channel) opens a file for reading into file descriptor f; get(f, x) reads a value from f into x; put(f, x) writes the value of x to f; close(f) closes the file. The layout operators: newline writes a line break; space writes a space; newpage starts a new page. Format-directed transput: readf(format, items) and writef(format, items) use a format expression to control the representation of values, analogous to C’s scanf/printf.

ALGOL 68’s parallel clause for concurrent execution: par begin A, B, C end specifies that the enclosed clauses A, B, C execute concurrently. This is ALGOL 68’s structured concurrency mechanism (unlike ALGOL 60 extensions that used go to-based concurrency). Mutual exclusion in ALGOL 68: the semaphore type sema provides a counting semaphore; up(s) increments the semaphore (signal/V); down(s) decrements the semaphore and blocks if the count is zero (wait/P). The level(s) procedure reads the current semaphore count without blocking. ALGOL 68 semaphore design for mutual exclusion: initialize a semaphore with s := level 1 (binary semaphore); every concurrent clause that accesses a shared variable executes down(s) before the access and up(s) after. The retainer task of ALGOL 68 parallel clause design: identify all shared ref variables accessed in the parallel clause; design semaphore guards for each shared variable or shared-variable group; verify that every access path through each parallel clause performs matching down/up pairs; and test with concurrent access patterns that expose missing or mismatched guard placements.

ALGOL’s design legacy: structured programming and the language family tree

ALGOL’s historical significance is its central role in establishing structured programming as the dominant programming paradigm and its influence on the entire subsequent landscape of programming language design. ALGOL 60’s specific contributions: block structure with lexical scoping (adopted by Pascal, C, Ada, Modula-2, and virtually every subsequent procedural language); recursive procedures as a first-class feature (absent from early FORTRAN and COBOL, which defined an era of programming without recursion); the for/step/until/do loop form (the direct predecessor of Pascal’s for, C’s for, and Ada’s for); the if/then/else conditional expression (the direct predecessor of C’s ternary operator and if/else); and the call-by-name parameter binding mode (adopted in spirit by C++ references and Haskell’s lazy evaluation, and directly by macro systems in many languages). The Backus-Naur Form notation (BNF) used to specify ALGOL 60’s grammar became the standard notation for context-free grammars used in compilers, parser generators, and language specifications ever since.

The ALGOL family of languages: Pascal (Niklaus Wirth, 1970) is the most direct descendant of ALGOL 60, cleaning up the language design and adding structured data types (records, enumerated types, sets). Ada (DoD, 1983) descended from Pascal with ALGOL 68 influences, adding strong typing, packages, tasks for concurrency, and generics. Modula-2 (Wirth, 1978) and Oberon (Wirth, 1987) are later descendants of Pascal and ALGOL 60, emphasizing modular programming. C is not a direct descendant of ALGOL but was heavily influenced by the CPL → BCPL → B lineage, itself influenced by ALGOL. Java and C# are C-family languages that incorporate ALGOL 60’s block structure and scoping rules through their C heritage. The direct ALGOL 68 descendants include S3 (ICL’s systems programming language), Turing (University of Toronto, 1982), and various academic languages. ALGOL 68 itself influenced the design of C++ through the Stroustrup connection: Bjarne Stroustrup cites ALGOL 68 as a significant influence on C++ features including references, multiple assignment, and operator overloading.

How HourTab tracks ALGOL developer retainer hours

ALGOL retainer work shares the invisible-work problem common to all language engineering retainers, with the additional challenge that ALGOL’s most important retainer tasks — static scoping outer-scope variable sharing analysis, call-by-name aliasing diagnosis, Jensen’s device implementation design, ALGOL 68 mode declaration and union-case design, and parallel clause semaphore guard engineering — produce diffs whose surface area is small relative to the analytical work required. Restructuring a shared outer-scope accumulator to use a formal parameter is a diff with one parameter declaration and one call-site addition; the value is correct independent accumulator bindings for every activation of the compound procedure, elimination of inter-activation state corruption for all concurrently active instances, and a parameter-passing design that is portable to ALGOL implementations with any activation record layout. Changing a call-by-name index parameter to call-by-value by adding it to the value list is a diff with one identifier in one declaration; the value is frozen index semantics at call time, elimination of call-by-name aliasing between the array subscript and the index parameter, and correct array element reads for all activations regardless of index mutation after the call.

HourTab gives ALGOL developers a public retainer-hours URL they send to clients — typically university computing centers and national laboratory groups maintaining ALGOL-based numerical computing codebases (ALGOL 60 was the dominant language for scientific computing in European universities through the 1970s and remained in use at some institutions through the 1980s), programming language research groups working with ALGOL 68 implementations (Algol68C, FLACC, a1, Marst, and the open-source Algol 68 Genie), and compiler engineering teams studying ALGOL language design for historical and educational purposes — at the start of an engagement. For ALGOL retainers, each work log entry should name the mechanism (begin...end compound statement block structure authorship; local variable declaration and static scoping correctness audit; go to statement label and non-local exit design; call-by-value parameter definition with argument-copy semantics; call-by-name parameter re-evaluation semantics and Jensen’s device design; call-by-name aliasing analysis and aliasing-free parameter mode audit; for/step/until/do loop design; if/then/else compound conditional; recursive procedure declaration inside block; ALGOL 68 mode declaration design; ALGOL 68 transput I/O read/write/print/get/put; ALGOL 68 union and case variant type design; ALGOL 68 par begin...end parallel clause; sema/up/down semaphore operations), the specific procedure name and the scoping or parameter mode problem, and the before/after observable metric. ALGOL retainers are often compared to BCPL developer retainers for languages-that-shaped-C historical engineering work, to Simula developer retainers for 1960s-era language engineering with concurrent activation patterns (Simula’s PROCESS coroutines share ALGOL 60’s scoping model and were themselves implemented in ALGOL 60), and to Scheme developer retainers for lexical scoping analysis in languages where the scoping model is the central design concern. HourTab’s work log makes the static scoping analysis, call-by-name aliasing diagnosis, and parameter binding mode restructuring visible to clients who would otherwise see only the symptom — wrong integration results or wrong array element reads — and not understand why the fix required understanding ALGOL 60’s block structure rules and call-by-name parameter semantics.

Track ALGOL developer retainer hours without the status emails

HourTab gives ALGOL developers a public URL per client retainer. One link, no login, live burn-down. Your clients stop asking “how many hours do I have left?” and your work log becomes the proof of value that gets the retainer renewed.

See HourTab pricing →

FAQ: ALGOL developer retainers

What does an ALGOL developer on retainer typically do?

An ALGOL developer on monthly retainer covers four principal service areas: block structure and static scoping analysis (begin...end compound statement authorship; local variable declaration and static scoping correctness audit; outer-scope variable sharing analysis across concurrent activations; go to statement label design; non-local exit with block-exiting labels); parameter binding mode design (call-by-value parameter definition with argument-copy semantics; call-by-name parameter re-evaluation semantics; Jensen’s device parameterized summation design; call-by-name aliasing analysis; parameter mode audit for all procedure declarations); ALGOL 60 structured programming constructs (for/step/until/do loop design; if/then/else compound conditional; recursive procedure declaration inside block; nested block static scoping); and ALGOL 68 extensions (mode declaration design; transput I/O; union/case variant types; par begin...end parallel clause; sema/up/down semaphore operations).

What ALGOL work is most commonly underlogged in a retainer?

Static-scoping outer-scope variable sharing repair (outer-scope accumulator shared between two concurrent compound procedure activations; 5 wrong integration results/run; restructured to pass value as formal parameter; wrong results: 5/run → 0; 15–28 hrs invisible in scoping analysis and parameter restructuring), call-by-name aliasing repair (call-by-name array subscript a[i] and index i parameters aliased by index mutation inside procedure; 6 wrong array elements/run; changed i to call-by-value; wrong elements: 6/run → 0; 12–22 hrs invisible in call-by-name aliasing analysis and parameter mode audit), and Jensen’s device design (call-by-name summation with constant expression as summand defeated re-evaluation; redesign to pass array subscript expression; wrong sums: 4/run → 0; 10–18 hrs invisible in call-by-name semantics audit and Jensen’s device implementation).

What are typical ALGOL developer retainer rates?

Entry-level ALGOL developers (1–2 years, begin...end block structure, basic procedure declarations, for/step/until/do loops, if/then/else conditionals) bill at $70–$125/hr. Mid-level ALGOL engineers (2–4 years, static scoping analysis for nested block correctness, call-by-name vs call-by-value parameter mode design, Jensen’s device implementation, ALGOL 68 mode declarations and transput, concurrent section design with semaphores) bill at $120–$215/hr. Senior ALGOL architects (4–8 years, complete ALGOL program architecture, complex nested block scoping design, call-by-name aliasing analysis, ALGOL 68 parallel clause engineering, ALGOL compiler toolchain work) bill at $175–$320/hr. Monthly retainer ranges: $2,000–$5,500/mo advisory (15–25 hrs), $7,500–$19,000/mo for full ALGOL platform engagements.

What should an ALGOL developer retainer agreement include?

An ALGOL developer retainer agreement should specify: block structure scope (begin...end compound statement authorship; local variable declaration and static scoping correctness audit; outer-scope variable sharing analysis; go to label design); parameter binding mode scope (call-by-value parameter copy-semantics design; call-by-name re-evaluation semantics and Jensen’s device; call-by-name aliasing analysis; parameter mode audit); structured programming scope (for/step/until/do loop design; if/then/else with begin...end body; recursive procedure declaration inside block); ALGOL 68 scope if applicable (mode declaration design; transput I/O; union/case variant types; par begin...end parallel clause; sema/up/down semaphore operations); and hour logging format (procedure name; operation type — scoping analysis, parameter mode, structured control flow, or ALGOL 68 extension; before/after error metric; ALGOL version).

How should ALGOL developer retainer hours be logged?

Log each ALGOL retainer session with: advisory category (begin...end block structure authorship; local variable declaration and static scoping audit; go to label and non-local exit design; call-by-value parameter copy-semantics; call-by-name parameter re-evaluation semantics and Jensen’s device; call-by-name aliasing analysis; for/step/until/do loop; if/then/else conditional; recursive procedure declaration; ALGOL 68 mode declaration; ALGOL 68 transput I/O; ALGOL 68 union/case; ALGOL 68 parallel clause; sema/up/down), the specific procedure name and scoping/parameter mode problem (outer-scope accumulator shared between two activations; 5 wrong integration results/run; restructured to formal parameter; wrong results: 5/run → 0), and the before/after metric. Include ALGOL version (ALGOL 60 or ALGOL 68), implementation, and whether the fix required scoping restructuring, parameter mode change, Jensen’s device redesign, or ALGOL 68 mode declaration correction.