Blog › ICP guides

Simula developer on retainer: IS prefix inheritance, DETACH/RESUME coroutines, NEW allocation, and Simula 67 on monthly retainer

October 3, 2026 · ~14 min read

A Simula developer was maintaining a legacy discrete-event simulation of a manufacturing process flow model in GNU Cim (the GNU Simula compiler). The simulation modeled a factory floor: jobs arrived at a queue, were assigned to machines for processing, and moved to output queues on completion. The core queue class was derived from Simula’s built-in LINK class: each queue element was an object that could be inserted into and removed from HEAD-based linked queues using INto and OUt. The simulation ran for 24 simulated hours, and the factory’s steady-state queue lengths and throughput rates had been validated against the physical factory’s production logs.

The developer was asked to add a priority mechanism to the job queue: high-priority jobs should cut to the front of the queue rather than joining at the back. The developer extended the base JobQueue class using Simula’s IS PREFIX syntax — declaring PriorityJobQueue IS JobQueue BEGIN ... END, which creates a subclass that inherits all of JobQueue’s attributes and methods, with the subclass body executing via the base class’s INNER call. The developer added priority comparison logic in the subclass body: before inserting a new job into the queue, the subclass examined the job’s priority field and used INto_head for high-priority jobs instead of the base class’s default INto. The logic was correct in isolation.

What actually happened: the base class JobQueue body, inherited via the IS PREFIX mechanism, contained a DETACH call after its INto operation. DETACH is Simula’s coroutine suspension primitive: it transfers control from the currently executing process back to the simulation’s main event loop, which then selects the next event to execute. In the original JobQueue, the DETACH fired after every job insertion, correctly yielding control to the scheduler. In the PriorityJobQueue subclass, the INNER call in the base class body executed the subclass body — which performed the priority check and called either INto_head or INto directly, then returned from INNER. After INNER returned, the base class continued its body and reached its own DETACH. But because the subclass had already inserted the job using its own INto call (bypassing the base class’s INto), the base class’s subsequent INto performed a second insertion of the same object into the queue, creating a duplicate entry. Over 24 simulated hours, 3 queue read operations returned wrong front-of-queue elements because they were reading duplicate queue entries rather than the actual next job. Wrong front-of-queue reads: 3 → 0 after restructuring the subclass to perform its priority insertion and then call DETACH itself (before reaching the base class’s INto), and modifying the base class to use a guard that skips its own INto when the subclass has already inserted.

The reason this class of bug is systematically invisible in Simula is that the IS PREFIX inheritance mechanism executes the base class body and the subclass body as a single coroutine, with INNER as the call point. The sequencing of operations between the base class body and the subclass body is not obvious from reading either in isolation: understanding what happens requires mentally composing the two bodies at the INNER boundary, tracking where each DETACH fires, and verifying that no side effects (like queue insertions) are performed twice. GNU Cim produces no warning about duplicate INto calls or about a process accumulating in the event list without a matching DETACH. The simulation runs to completion; the wrong queue reads appear only for specific jobs in specific timing conditions.

Simula 67: the first object-oriented language and its simulation heritage

Simula 67 was designed by Ole-Johan Dahl and Kristen Nygaard at the Norwegian Computing Center (Norsk Regnesentral) in Oslo, building on their earlier SIMULA I (1962–1964) and on ALGOL 60 (which Simula 67 extends syntactically and semantically). It is universally recognized as the first object-oriented programming language: it introduced the concept of a class (as a generalization of ALGOL 60 procedure blocks that persist as objects), instantiation of classes into objects via NEW, inheritance via the IS PREFIX syntax, and virtual procedures (VIRTUAL) that can be overridden in subclasses. Every object-oriented language that followed — Smalltalk (Alan Kay at Xerox PARC, who explicitly credited Simula as the inspiration for Smalltalk’s message-passing model), C++ (Bjarne Stroustrup, who learned OOP from Simula while at Cambridge), Java, Python — descends intellectually from Simula 67’s class/object/inheritance model. Alan Kay’s oft-quoted remark that “the big idea is messaging” was a deliberate departure from Simula’s structural inheritance approach, making the Smalltalk/Simula divergence the first architectural debate in object-oriented design.

The IS PREFIX syntax is Simula’s inheritance mechanism. A class declaration CLASS B IS A BEGIN ... END declares class B as a subclass of A: B inherits all of A’s attributes (instance variables), procedures, and class body. When an object of type B is instantiated with NEW B, the runtime executes A’s class body, which contains an INNER call — the point at which execution transfers to B’s class body. When B’s body returns, control returns to A’s body at the point after INNER. This is fundamentally different from constructor chaining in Java or C++: it is a coroutine-based composition model where the prefix class body and the subclass body are interleaved at the INNER boundary. Virtual procedures (VIRTUAL: PROCEDURE name) allow the subclass to provide a procedure that overrides the prefix class’s declaration; the virtual procedure mechanism is the precursor to virtual functions in C++ and abstract methods in Java. The IS type inspection operator (obj IS ClassName) and QUA type coercion operator (obj QUA ClassName) allow safe runtime type checking and down-casting, directly anticipating Java’s instanceof and C++’s dynamic_cast.

The NEW operator allocates a new instance of a class: NEW JobQueue returns a reference to a newly initialized JobQueue object. Simula uses reference semantics: variables of a class type hold references (not values), and the NONE value (Simula’s null) represents the absence of a reference. Reference assignment copies the reference, not the object. The INSPECT ... DO ... OTHERWISE ... construct is Simula’s safe-dereference pattern: it checks whether a reference is NONE and executes the DO branch if the reference is valid, or the OTHERWISE branch if it is NONE. This is directly analogous to optional chaining in modern languages. Memory management in Simula 67 is garbage-collected: objects are reclaimed when there are no remaining references to them. The simulation event list holds references to all active process objects; a process that fails to call DETACH or PASSIVATE retains a reference in the event list, preventing collection and accumulating as an unreachable-but-referenced object that the scheduler eventually attempts to schedule.

The discrete-event simulation framework, built into Simula via the SIMULATION class (which must be the outermost prefix in any simulation program), provides the event list, the process scheduling machinery, and the HOLD, PASSIVATE, ACTIVATE, DETACH, and RESUME primitives. HOLD(t) suspends the current process for t simulated time units and schedules it to resume after that delay. PASSIVATE suspends the current process indefinitely, removing it from the event list; it must be explicitly reactivated by another process via ACTIVATE obj AT time or ACTIVATE obj DELAY t. DETACH suspends the current process and transfers control to the MAIN process (the simulation’s top-level process); it is used when a process has completed its current activity and should yield to the scheduler without being permanently passivated. RESUME(obj) transfers control directly to a specific process object, bypassing the event list scheduler — a low-level coroutine transfer that should be used carefully in production code.

LINK queues, SQS, and process class patterns

The LINK class is Simula’s built-in doubly-linked list node class. Objects that inherit from LINK (via IS LINK or any prefix chain that includes LINK) can be inserted into and removed from HEAD-based circular doubly-linked queues. The HEAD class represents a queue header; LINK objects are inserted with INto(head) (at the tail) or INto_head(head) (at the front), and removed with OUt (removes the LINK object from whichever queue it is currently in). The FIRST and LAST attributes of a HEAD give the first and last elements, and SUC and PRED give the successor and predecessor within the queue. Queue navigation requires traversal via SUC pointers and NONE checks. A LINK object may only be in one queue at a time; inserting a LINK that is already in a queue into another queue removes it from the first queue first — a property that produces silent data loss if a developer inserts the same object twice expecting two copies in the queue.

The SQS (Simula Queueing System) is a set of utility classes layered on top of LINK and HEAD that provide standard queue, priority queue, and set abstractions. SQS was developed at the Norwegian Computing Center and is available in GNU Cim. A retainer bug in SQS priority queues: a developer adds items using priority_INto but expects lower numerical priority values to sort to the front of the queue; the SQS implementation sorts by ascending priority value (lower number = higher priority), so using a priority field where higher numbers mean more urgent produces an inverted queue order. The bug is invisible in testing if all test items have distinct priority values and the failure only manifests when the queue serves items in the wrong order under time pressure. Work log entry: “PriorityJobQueue:priority_INto: priority field uses 1=highest priority, 9=lowest; SQS sorts ascending (lowest value first); high-priority jobs inserted with value 1 sort to front correctly; bug is in downstream comparison: validator reads FIRST expecting highest numerical value, gets lowest; 4 wrong priority evaluations; fix: inverted comparison in validator; wrong evaluations: 4 → 0; 2h.”

Process class debugging is the dominant category of Simula retainer work in discrete-event simulation. A process class in Simula is a class that is prefixed by PROCESS (which is itself prefixed by LINK, allowing process objects to be inserted into simulation queues): the process class body describes the lifecycle of a simulated entity over time. A machine process might look like: claim a job from the input queue; HOLD(processing_time) to simulate machining; place the job in the output queue; repeat. The process body executes as a coroutine: it runs until it calls HOLD, DETACH, or PASSIVATE, at which point control returns to the simulation scheduler. The scheduler selects the next event to execute based on the event list, which is ordered by simulated time. A retainer bug in process class coroutine design: a developer uses DETACH where PASSIVATE is semantically correct — DETACH transfers to MAIN but does not remove the process from the event list, so it may be scheduled again at the current simulated time even though it has no work to do, producing spurious re-entry into the process body. The correct choice between DETACH, PASSIVATE, and HOLD(0) depends on whether the process should re-enter its body at the same simulated time, be reactivated by another process, or yield to the next future event.

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

IS prefix inheritance DETACH timing bugs are the largest category of Simula retainer work that produces no visible artifact. The pattern follows the same shape: a base class has a correctly designed class body with DETACH calls at the right simulation time points; a developer extends the class using IS PREFIX; the subclass body executes via INNER and performs additional operations (queue insertions, state updates) that interact with the base class body’s operations after INNER returns; the composition produces double insertions, missed DETACH calls, or incorrect event list state. The GNU Cim compiler produces no warning about this interaction. The simulation runs without raising exceptions; the wrong behavior appears in the output statistics as slight throughput discrepancies or occasional wrong queue reads that look like timing noise. Work log entry: “PriorityJobQueue IS JobQueue: subclass body calls INto_head before returning from INNER; base class body after INNER calls its own INto unconditionally; job object inserted twice (second insertion removes it from head position and adds to tail, per LINK single-queue constraint); 3 wrong front-of-queue reads over 24h simulation; fix: added guard in base class body to skip INto if object already in queue (IF NONE =/= head THEN INto(queue)); added subclass DETACH after INto_head; wrong reads: 3 → 0; 3.5h.”

VIRTUAL procedure override bugs are the second category. A prefix class declares a VIRTUAL: PROCEDURE name and provides a default implementation as a labeled declaration after the virtual list. A subclass may override the virtual procedure by redefining it in the subclass body. A retainer bug: the subclass developer provides a procedure with the same name but forgets to list it in the subclass’s own virtual declaration (required in some Simula implementations to properly override); the base class’s default implementation runs instead of the subclass’s. Alternatively: the developer misspells the procedure name in the subclass (Simula identifiers are case-insensitive, so Process_Job and process_job are the same, but ProcessJob and Process_Job are different); the override does not take effect; the base class default runs. Work log entry: “VIRTUAL: PROCEDURE compute_priority in BaseQueue prefix class; PriorityQueue subclass defines PROCEDURE compute_priority_extended instead of compute_priority; base class default compute_priority always returns 5; priority assignments all identical; 8 wrong priority values; fix: renamed subclass procedure to match virtual declaration; wrong values: 8 → 0; 1.5h.”

NONE reference access bugs are the third category. In Simula, dereferencing a NONE reference raises a runtime error (ERROR: Illegal dereference of NONE in GNU Cim). A retainer bug: a process iterates over a queue using SUC pointers, expecting the iteration to stop when it reaches the queue header (HEAD); the developer writes WHILE current =/= NONE DO ... but the queue is circular and current will never be NONE — it cycles back to the first element. The iteration runs indefinitely, consuming all simulation time, or until a resource limit terminates the simulation. The correct termination condition is WHILE current =/= head DO ... (compare to the queue header, not to NONE). Work log entry: “scan_queue procedure: loop condition current =/= NONE on circular LINK queue; loop does not terminate; simulation hangs at first queue scan; fix: changed condition to current =/= queue_head; simulation completes in expected time; infinite loops: 1 → 0; 1h.”

Track Simula developer retainer hours without the status emails

When a 3.5-hour session diagnoses a DETACH timing bug in a PriorityJobQueue IS JobQueue IS prefix subclass — tracing the INNER call boundary, identifying that the base class performs a second INto insertion after INNER returns, restructuring the guard condition, and verifying that all three wrong queue reads now return correct results over a 24-hour simulation run — the work log must name the class, the INNER call boundary, and the wrong-read count before and after. HourTab gives your Simula retainer client a public dashboard URL they can bookmark: hours used, hours remaining, and a work log that names the class and the INNER composition problem. No client login. No status emails. CSV in, URL out.

See HourTab pricing →

How HourTab tracks Simula developer retainer hours

Simula retainer work is invisible by the same mechanism that makes IS prefix DETACH timing bugs dangerous: the GNU Cim compiler sees no inconsistency between the base class body and the subclass body when composed at the INNER call boundary. The compiled simulation is syntactically and semantically valid Simula 67. The simulation runs without exceptions. The wrong behavior — three incorrect queue reads over a 24-hour simulation run — looks like random noise in the simulation output, indistinguishable from statistical variation in the manufacturing process model. Distinguishing a software bug from simulation-model variance requires either formal verification of the queue class composition or systematic comparison of multiple simulation runs with deterministic seed values, neither of which leaves an artifact that explains the investigation.

The work log needs to name the mechanism: which class, which IS PREFIX chain, where the INNER call boundary was, what the base class did after INNER returned, what the double-insertion interaction was, and what the wrong-read count was before and after the fix. A log entry that says “fixed queue bug in Simula simulation, 3.5h” is not auditable. A log entry that says “PriorityJobQueue IS JobQueue: subclass calls INto_head(queue) inside INNER; base class calls unconditional INto(queue) after INNER returns; LINK single-queue constraint removes object from head and reinerts at tail; queue scan reads tail entry as front-of-queue for 3 events over 24h simulation; fix: guard in base class checks IF obj.PRED == NONE THEN INto(queue); wrong reads: 3 → 0; 3.5h” is auditable.

HourTab gives Simula developers a public retainer-hours URL they send to clients — legacy manufacturing simulation systems at industrial companies, academic computer science departments maintaining historical simulation models, defense contractors running legacy logistics optimization systems in Simula, and engineering consultancies supporting discrete-event models built before the widespread adoption of modern simulation platforms. For Simula retainers, each work log entry should name the mechanism: which class, which IS prefix chain, where the INNER call boundary is, and what the queue state was before and after the DETACH timing correction. Comparative context: Simula retainer work has structural overlap with related paradigms — ALGOL retainers cover procedure-level bugs in Simula’s direct ancestor language, where ALGOL 60’s block structure and procedure nesting are the direct predecessors of Simula’s class body and INNER mechanism; Smalltalk retainers cover message-sending and method lookup bugs in the language that Simula 67 most directly inspired, where Alan Kay’s message-passing model replaced Simula’s structural inheritance with a dynamic dispatch mechanism; and Scheme retainers cover coroutine and continuation bugs in a language whose call/cc mechanism provides the same “suspend execution and transfer control” capability that Simula’s DETACH/RESUME provides, but expressed as first-class continuation objects rather than simulation-layer primitives.

FAQ: Simula developer retainers

What does a Simula 67 developer on retainer typically do?

A Simula 67 developer on monthly retainer covers DETACH timing diagnosis in IS prefix subclass extensions (identifying where a subclass body performs queue insertions or state changes inside INNER that interact incorrectly with base class operations after INNER returns, producing double insertions or missed DETACH calls); LINK queue debugging (INto/INto_head/OUt operation ordering, circular queue termination conditions, and single-queue constraint violations); VIRTUAL procedure override verification (confirming that subclass procedure names match the virtual declarations in the prefix class exactly); NONE reference access diagnosis (identifying circular queue traversal that compares to NONE instead of the queue header); and process scheduling model validation (confirming that HOLD/PASSIVATE/DETACH choices match the intended scheduling semantics for each process class).

What Simula work is most commonly underlogged?

DETACH timing bugs in IS prefix subclass extensions are the most systematically underlogged Simula retainer work. When a developer extends a queue or process class using IS PREFIX, the base class body operations that follow the INNER call continue to execute after the subclass body returns, and those operations may interact with what the subclass did inside INNER in ways that produce incorrect queue state. There is no GNU Cim warning, no runtime exception until a dereference fails, and the wrong behavior surfaces only in simulation output statistics as subtle throughput discrepancies or occasional wrong queue reads. Diagnosis requires composing the base class and subclass bodies mentally at the INNER boundary and tracing the event list state through each DETACH/RESUME cycle. Two to five hours invisible per occurrence.

What are typical Simula developer retainer rates?

Entry-level Simula developers with experience in GNU Cim, basic CLASS/OBJECT patterns, and discrete-event simulation fundamentals typically bill at $70 to $125 per hour. Mid-level Simula programmers with experience in IS prefix inheritance, LINK queue classes, and DETACH/RESUME/ACTIVATE coroutine patterns typically bill at $100 to $175 per hour. Senior Simula developers with deep knowledge of Simula 67 semantics, the SQS simulation kernel, simulation model validation, and legacy system integration typically bill at $145 to $265 per hour. Monthly retainer ranges: $1,500 to $2,800 per month for advisory engagements covering IS prefix hierarchy reviews and DETACH timing audits (10 to 18 hours per month); $2,200 to $5,500 per month for active maintenance including process model restructuring and simulation queue debugging.

What should a Simula developer retainer agreement include?

A Simula developer retainer agreement should specify: implementation scope (GNU Cim is the only actively maintained open-source Simula 67 compiler; differences from historical NCC/Norsk Data implementations may affect legacy behavior); IS prefix and INNER scope (whether the retainer covers auditing IS prefix class hierarchies for INNER call placement and post-INNER operation interactions); LINK and HEAD/TAIL queue scope (whether INto, OUt, INto_head, OUt_head, and SUC/PRED traversal operations in LINK queue classes are in scope); DETACH/RESUME/ACTIVATE scope (whether the coroutine scheduling mechanism, including HOLD vs PASSIVATE vs DETACH choice, is in scope); VIRTUAL procedure override scope; and simulation validation scope (whether verifying simulation output against expected steady-state distributions is in scope).

How should Simula developer retainer hours be logged?

Log each Simula retainer session with: the class name and IS prefix chain (e.g., PriorityJobQueue IS JobQueue IS LINK); the INNER call boundary interaction that produced the bug (e.g., subclass calls INto_head inside INNER; base class calls unconditional INto after INNER; LINK single-queue constraint removes object from front and re-inserts at tail); the simulation time and queue state at which wrong behavior first appeared (e.g., simulation time 4.5h, queue length 7, front read returned second entry); the symptom with count (e.g., 3 wrong front-of-queue reads over 24h run); and the fix (e.g., added guard in base class; added subclass DETACH; wrong reads: 3 → 0; 3.5h). For VIRTUAL override bugs: the virtual procedure name, the mismatch in the subclass, and the corrected name.