Skip to content

Open Recursion

A shared object method calls through its current receiver so specialized receiver behavior can determine the internal call's target.

Version
v1 · 2026-10-31 · History
Domain-specific #
14543
Domain group
Applied Sciences & Engineering
Origin domain
Computer Science & Software Engineering

Core Idea

Open recursion allows shared object behavior to invoke another operation on the current receiver, with the target selected using that receiver's specialized behavior. A base method can thereby invoke a subclass override; a method found through delegation can send to the original child receiver. The shared method does not have to name the eventual target in advance.[1][2]

The relation relies on the same receiver being available inside the shared method and a lookup or dispatch rule that can select a specialization. The term describes self-reference in an extensible object structure; one execution need not call itself repeatedly or terminate through a base case.

How would you explain it like I'm…

Do Your Own Version

Imagine a teacher's rule for all kids: 'Now do your own dance.' Each kid who follows the rule does their own special dance, not the teacher's. In computer programs, open recursion lets a shared instruction say 'do your own version' and get the special version of whoever is running it.

Calling Your Own Special Version

In programming, objects can share instructions from a more general version, like a 'parent' class, while also having their own special versions of some steps. Open recursion means a shared instruction can call another step on the same object, and the computer picks that object's own special version. So the general code doesn't need to know ahead of time exactly which version will run. Despite the name, it doesn't have to mean a function calling itself over and over.

Late-Bound Self Dispatch

Open recursion is a feature of object-oriented programming where shared code can call another operation on the current object (often called self or this), and the version that runs is chosen based on that object's specialized behavior. For example, a method in a base class can call a method that a subclass has overridden, and the subclass's version runs. Similarly, with delegation, a method found on a parent object can still send messages to the original child object. The shared code never has to name the exact target in advance. It needs two things: the same receiving object available inside the shared method, and a lookup rule that can pick the specialization. 'Recursion' here refers to self-reference, not to a function repeatedly calling itself until a base case.

 

Open recursion lets shared object behavior invoke another operation on the current receiver, with the target selected by that receiver's specialized behavior. In class-based languages a base-class method can call a method that a subclass overrides, dispatching to the override; in delegation-based systems a method found on a parent can send messages to the original child receiver. The shared method therefore does not name its eventual target in advance. The mechanism requires two ingredients: the same receiver must be available inside the shared method (as self or this), and a lookup or dispatch rule must be able to select a specialization based on that receiver. The term describes self-reference within an extensible object structure. It does not imply that any particular execution calls itself repeatedly or terminates through a base case.

Structural Signature

  • Shared method: behavior provided by a base class or parent object.
  • Current receiver: the object on which the outer operation runs.
  • Internal self-send: the shared method invokes an operation on that receiver.
  • Receiver-sensitive lookup: dispatch can choose a specialized target for that same receiver.
  • Variation: a receiver's override or supplied method changes the target without editing the shared method.
  • Boundary: language rules decide which calls permit this dispatch.

What It Is Not

An arbitrary virtual call to another object is dynamic dispatch but does not establish an internal self-send. A fixed private or explicitly directed base call does not exhibit the same receiver-sensitive interception. The selection of JavaScript this at a call site is a separate receiver-binding question.

Scope of Application

Class-based overriding and prototype-style delegation can each implement this pattern. The exact dispatch rules, available specializations, and treatment of private, static or explicit-super calls depend on the language.

Clarity

Separating the outer method's location from its receiver explains why an inherited method can invoke behavior defined by a more specific object.

Manages Complexity

A shared operation can be reused while an accessor or hook changes. This localizes customization but can make internal implementation choices observable to subclasses.

Abstract Reasoning

Ask what happens if the same shared method runs on a different compatible receiver: a change in the selected internal target demonstrates the dispatch role. A method that sends only to a fixed helper does not pass that test.

Knowledge Transfer

The same question helps compare class overrides and delegation without assuming their object models or source syntax are identical.

Examples

CountingSet and a changed base method

Aldrich's CountingSet overrides add and addAll; the latter invokes the base addAll and increases its counter by the collection size. If the base implementation is changed to call add per element, the internal calls dispatch on the same CountingSet receiver to its overriding add, incrementing the counter a second time. This is the paper's hypothetical implementation change, not a documented production incident.[1]

Mapped back: shared method Set.addAll; receiver the subclass instance; internal operation add; selected target CountingSet.add. Aldrich's proposed selective scheme makes non-open internal calls static while leaving external calls dynamically dispatched.

Delegating points

In Tanter's teaching system a point parent supplies add, whose body sends x? to self. random-point delegates to point and supplies x? as a random computation. A send of add to random-point uses the shared parent method while its internal x? resolves to the child. The example's random output is not a fixed prediction; the notation is the teaching system's, not Self syntax.[2]

Mapped back: shared method point.add; receiver random-point; internal operation x?; target the child accessor. Binding lookup to the parent alone would not find an accessor on this parent, an inference from the presented definitions. Ungar and Smith separately describe a related Self point design.[3]

Structural Tensions

The CountingSet example shows a concrete tradeoff: interception offers extension yet exposes a base implementation choice to a subclass. This is a risk in the illustrated design, not an inevitable defect of every open call.[1]

Structural–Framed Character

The structural relation is receiver-preserving extensible lookup, independent of the particular point or set. Its language and evaluation arise in programming design: openness can be valuable for extension and costly for encapsulation depending on intent. The vocabulary travels among object models, while its origin and practical meaning are tied to programming language design. A reader recognizes the pattern in an existing method arrangement rather than importing the name as a prerequisite. Its character: a structural mechanism framed by programming practices and design choices.

Structural Core vs. Domain Accent

The portable core is shared behavior referring back to an extensible receiver. Receiver identities, method lookup, class overrides and delegation are programming-specific realizations. Recursive descent reduces a problem through smaller cases toward a base case; open recursion does not necessarily do either. This named mechanism remains specific to receiver-oriented programming.

Inheritance captures default transfer and override along a lineage; it does not by itself require an internal self-send. Open recursion may arise in the class case or through delegation, so the present comparison does not establish a necessary typed parent.

Neighborhood in Abstraction Space

Open Recursion sits in a sparse region of the domain-specific corpus (98th percentile for distinctiveness): few abstractions share its structure, so a faithful description tends to retrieve it precisely.

Family — Unclustered & Miscellaneous (2835 abstractions)

Nearest neighbors

Computed from structural-signature embeddings · 2026-10-31

Not to Be Confused With

  • Receiver binding: selecting which object this denotes answers a different question from how a subsequent internal method call is dispatched.
  • Ordinary recursive descent: repeated reduction to a base case is not required here.
  • All dispatch: selection for an unrelated receiver lacks the same-receiver role.

References

[1] Jonathan Aldrich, “Selective Open Recursion A Solution to the Fragile Base Class Problem”, figure 1 and §§1.1–2, directly inspected PDF text. registry ↩a ↩b ↩c

[2] Éric Tanter, “Forwarding and Delegation,” Object-Oriented Programming Languages: Application and Interpretation, §§4.3.2 and 4.4, directly inspected online textbook. registry ↩a ↩b

[3] David Ungar and Randall B. Smith, “Self The Power of Simplicity”, Lisp and Symbolic Computation 4, no. 3 (1991), §3 and figure 4, printed pp. 8–9, directly inspected PDF text and caption; figure layout not independently visually inspected. registry ↩