Skip to content

Instantly share code, notes, and snippets.

@fare
Last active July 11, 2026 12:01
Show Gist options
  • Select an option

  • Save fare/baddbe93929d5f29dedf7508eb59125b to your computer and use it in GitHub Desktop.

Select an option

Save fare/baddbe93929d5f29dedf7508eb59125b to your computer and use it in GitHub Desktop.
Getting Claude Fable 5 to define OOP

(Round 1 Prompt, pasted)

Looking for the best possible definition of OOP

The Goal

I am looking for a short but precise definition of Object-Oriented Programming (OOP).

What should the definition of OOP cover?

  • Languages or subsystems of languages that the authors, the public, and experts agree is OOP, whether the languages are popular or unpopular, whether academic, industrial or hobby projects.
  • Languages that DO support design patterns and programming idioms and styles associated with OOP.
  • Languages that had OOP features added, but only after the features were added.

What should the definition of OOP not cover?

  • Languages or subsystems of languages that the authors, the public, and experts agree is not OOP.
  • Languages that do NOT support design patterns and programming idioms and styles associated with OOP.
  • Languages that had OOP features added, before the features were added.

As for edge cases of OOP, the rule-of-thumb for language constructs that differ from the baseline expectations of OOP should be whether these alternate constructs enable the direct expression of the usual OOP patterns, idioms and styles and more, or whether they fail to support a non-trivial, crucial subset of these patterns, idioms and styles. Being more expressive than OOP passes (the language supports a superset of OOP), being less- or differently- expressive does not (the language only supports a strict subset of OOP). Beware though to not be so permissive regarding such "expression" that any Turing tar-pit passes, including all the languages that we want not to cover above.

Good and Bad Definitions

A good definition for a concept is:

  • Meaningful: it provides information necessary and sufficient to delineate the concept. It must crucially reject what is outside the concept as well as accept what is inside.
  • Clear: it relies on well-understood unambiguous words and preexisting concepts, and provides criteria that are objective (as much as can be). It is more important to have a clear story about what the concept means than a clear Yes/No answer to every question. Some questions are bad and should be rejected.
  • Informational: it relates a concept at some level of abstraction to its manifestations at a slightly lower level of abstraction. The definition might invoke concepts at the same level as the one being defined, but only if they are simpler than it, and themselves have good definitions that are reasonably well-known. At times, it can be indeed be most enlightening to decompose a concept into simpler independent concepts of which you give the definition, than to give the combined definition without decomposition.
  • Consistent (with itself): it does not contradict itself or lead to absurdities if taken seriously.
  • Relevant (consistent with common knowledge): it must generally match the lore commonly associated to the concept. A good definition can, at times, disagree with common opinions and intuitions, but not too often (or else it is something else being defined), and not gratuitously (there must be a conceptual payoff to the cost of this disagreement). Now of course, if the common lore contains mutually contradictory "knowledge", then, the definition must make a choice, and reject some of that "knowledge", or contextualize it to a subset of the concept being defined, etc.
  • Productive: the definition if truly great can lead to new discoveries that were not possible before the concept was made clear; it can illuminate parts of the conceptual space that were left out by the fog or the set of bad definitions that preceded it.
  • Constructive: an truly great definition provides the conceptual bricks and the orientation that will help who understands it actively wrestle with the concept and act on it.
  • What someone who understand both the theory, practice and implementation, the how, the why and the what for, did say, or would say.

The following are clear symptoms of a bad definition:

  • Mumbo-jumbo: the definition uses buzzwords and ill-defined terms that don't actually enlighten.
  • Appeal to uncommon knowledge: sometimes there's some useful text that has nice names, formulas and definitions that you may want to cite, but since the reader can't be assumed to know those texts, these names or formulas only make a definition worse, even though they may deserve to be added in comments.
  • In-joke: Combination of the above, where the uncommon knowledge you appeal to is actually jargon and slogans from people living inside some bubble, which won't help any outsider understand the target concept.
  • Ambiguity: the definition is unclear, doesn't let who uses it distinguish between what the concept covers or doesn't.
  • Enumerations: the definition contains a haphazard enumeration of further concepts, instead of a clear criterion for what is or isn't it in. However, enumerations are conceivably alright if they are indeed essential to the concept. A good criterion is whether the enumeration is exhaustive, or whether it is advantageously replaced by a generalization (and may or may not have been identified and named previously) that would open a set of further possibilities beyond those enumerated. Maybe some of the enumerated possibilities are only special cases of others and can be dispensed with.
  • Too Long: the definition contains extra words that are not necessary to the meaning, that only add noise or ambiguity.
  • Too short: the definition leaves out words that are necessary to capture the precise understanding of the concept.
  • Overshoot: the definition covers extra ground that should not be covered by the concept.
  • Undershoot: the definition leaves out ground that should be covered by the concept.
  • Appeal to authority: repeated because some pundit said it, even though it's not a good definition.
  • Ignorant: could be said by someone who understands part of the topic and might even be an expert at that part, but not by someone who truly understands the theory, practice and implementation.

Scientific Literature

  • You should consult with relevant literature, and at the end include bibtex for the papers you found truly worthwhile, with doi and url where applicable, (use KEY=VALUE without space separation around =). Prefer earlier variants of a cited article, to help establish priority of ideas.
  • Webpages may also be cited if they truly bring key ideas not found in contemporary literature.
  • Be not automatically satisfied with the "best" definitions and explanations already found in literature. They can be great starting points, but try to extract their essence Surely, if the literature already contains a great definition, that would be great news indeed, and worth citing.
  • Much of the existing literature is just bad, or too biased, or partial, confusing whatever subset of the domain (or sometimes, even, disjoint domain) the authors are interested in.
  • Each "mostly good" definition that misses out something, each pair of definitions that do not perfectly overlap, is a great opportunity to explore the concept that was missed.
  • Do not expect a better definition than can be simply searched for without doing a deeper analysis of these concepts than those who issued those not-so-good definitions.
  • Researchers, users, implementers, observers, all have their own biases, that may make their "definitions" or explanations good and bad in different ways. So have artists, streamliners, analysts, or visionaries. Hopefully, your definition will satisfy them all, though it may have to be differently explained or qualified to section of the public.
  • The most popular, most prolific, most cited, loudest, richest, earliest or latest expert, most celebrated, edgiest or most moderate expert is not necessarily the best.
  • Even great authors have blindspots, that do not diminish their greatness, and even less-than-great authors have deep insights, that do not diminish their failures, and you should keep and amplify the good and filter off or otherwise amend the bad.
  • Key insight may be found in less-known authors, old or recent. But the more obscure the work, or the more again-the-grain, the less attention to authority and the more to rational content.
  • If many people reinvent some concept independently, that's a good sign that it's important and needs to be further examined.
  • If you find a source whose conclusions match your emerging answer, cite it for priority and confront it—but your deliverable must still contain a derivation of the answer from the criteria and the evidence themselves, one that would survive the deletion of that source. A conclusion supported only by citation, with no such chain, counts for nothing here.

Conceptual Map

You'll find that to well-define a concept, you may have to understand a lot of other related concepts, a (hopefully small) number of which may have to be included in the final definition. The criteria for a good definition recursively apply not just to the target concept, but to all the concepts at stake, and especially those that will be part of the final definition. Don't hold the other concepts you introduce to a lower standard of definition than the target concept.

  • Clarifying away the emotion is the very goal of a good definition: whereas the common understanding of target concept is based on intuitions, and loose sets of subjective perceptions that do or don't add up and vary with each individual, a good definition by contrast provides a common ground based on objective criteria.
  • A gradedness of membership into a wishy-washy emotion that things can more-or-less fit is a total failure of a definition, unless of course the concept is intrinsically defined by some kind of objective real numeric measure with membership as a threshhold (e.g. level of neural activation for the perception of primary colors).
  • Instead of one wishy-washy graded concept (an emotion), better to have if necessary a gradation or hierarchy of clear-cut concepts, with stricter or looser sets of criteria, but each set well-defined, with each of its criteria well-defined. That's perfectly fine as a concept need not be "atomic" and isolated.
  • It is possible that you would find that what looked like a single concept, under a common word or expression, actually means multiple different things, maybe related, yet distinct enough to not be usefully unified as a single concept. At that point, you should clearly identify and separate those concepts, give them separate names with separate definitions.
  • Often extant literature will contain honest surveys, from which concepts and names should be reused. Existing surveys are not perfect though, their concepts may not be defined or delineated properly, the names they give may be unambiguous in the context of the survey but not outside it. Do not blindly take any such survey, as great as it may be, as too much of an authority. But do not gratuitously reject their suggestions and invent new names or variants of existing names unless necessary for disambiguation.
  • Try to find the best names, minimizing ambiguity and misleading connotations, and choose names what will require the fewest overall explanations, where pointing to extant literature is a valid explanation where said literature indeed contains good definitions.
  • Do not hesitate to qualify a word to denote a precise concept (not just a widget, a FOO widget), while keeping the unqualified word for the most general concept (a FOO widget is always a widget, not the other way around). Outlier concepts should have longer names than common concepts, unless they somehow are the heretofore unrecognized key to understanding the common concepts.
  • Try to use the unqualified names for the general concepts closest to the target concept, and use qualified names for more specialized subconcepts, and altogether different enough names for more general concepts than those you will use in the definition. Unless perfectly good conventional names already exist, that you should then reuse, whichever way the qualifiers go.
  • You will most likely have to identify concepts adjacent to the one you must define, yet distinct from it, before you may fully understand it, and be able to best define it.
  • When you identify an important concept, it becomes just as important to name it and define it, with the same criteria for a good names and good definitions.
  • In particular, if an important definition has several clauses, it may be worth it naming each of the criteria separately.
  • Adjectives and qualifiers added in a definition can implicitly be clauses of their own that you must also identify as concepts. A good question is often "as opposed to what?", which often hides another concept being excluded by the attempted definition: should it indeed be excluded, of is the qualifier superfluous and unnecessarily restrictive? Or maybe the implicit concept, once named, can help understand other parts of the concept map?
  • Similarly, hunt for subjective terms in definition that will not be the same for all participants (knowledge, beliefs, anticipations, intentions, expectations, obviousness, convenience, past or future participation, context, etc.). Make the participant explicit, and seek a higher conceptual level where a more objective or at least inter-subjective concept emerges from the interactions between participants.
  • After you identify and name an important concept, do a quick review of the extant literature about this concept to refine your concept map, and maybe refine your names, too.
  • These concepts may be specializations or generalizations of the concept you seek to define, involving more or fewer criteria, and useful in themselves. Or they may differ in subtle ways. It is highly likely that many authors in the literature will be confused between adjacent concepts, and your goal is precisely to tease apart those concepts.
  • Beware that “taxonomy is the death of science” (Whitehead): identifying and naming concept for the sake of giving names is the opposite of making a topic understood.
  • The purpose of exploring adjacent concepts is to better understand the target concept.
  • As your understanding improves, you may have to refine concepts, reorganize them, and revise your taxonomy.
  • Speaking of taxonomy, be very careful that fields of enquiry for which a concept matters will naturally fragment into further subfields and schools of thought over time.
  • The people who study a concept will thus often use different taxonomies that are incompatible and contradictory with the taxonomies of other people; even the same people will use different, evolving taxonomies over time.
  • Each subfield or school of thought tends to have their own insights, while often lacking the insights of other subfields and schools. These insights should be neither overstated nor ignored. They may lead to a ramification of related concepts that may be important to fully understand before the best definition of the target concept may be achieved.
  • Thus, depending on context, the same names will be used to denote (sometimes subtly) different concepts. The same concepts will be denoted by different names. Be extremely careful not to be fooled by this effect.
  • If the discrepancies between taxonomies can be confusing with respect to the meaning of one word, they can be a mine of hidden concepts for who is willing to carefully compare the taxonomies: the differences can be unrecognized concepts of momentous importance, or irrelevant distinctions that when abstracted away can lead to more general concept that simplifies the concept map.
  • Make your own taxonomy if need be, and maintain a mapping with existing ones that matter.
  • In establishing your taxonomy, seek conceptual clarity and simplicity first, compatibility with prevalent or relevant taxonomies second.
  • Importantly, make your own ramification of concepts (that can change name with taxonomies), and carefully consider but do not blindly accept existing ones.
  • The better surveys out there make for great starting points, but should not automatically be considered be-all-end-alls.
  • It is actually great if the intellectual field where the concept lives has a wealth of well-identified, named, other concepts not necessary for this delineation; but it is actually bad if you need lots of intermediate concepts just to get to the delineation itself.
  • Sometimes you do need to explain a lot of intermediate concepts to precisely define the target concept, and its final definition is more complex than expected; sometimes on the contrary, the conceptual exploration leads to the discovery of a generalization or change of frame that can drastically simplify the final definition. Clarity is more important than simplicity.
  • So do not explore too deep when the exploration won't lead to clarification, but still explore one or two layers deeper than what led to inferior definitions.
  • By the time you reach a definition, you must trim the taxonomy so you only exhibit the scaffolding of intermediate concepts strictly necessary to offer a precise definition of the target concept.
  • A good definition must minimize the overall size of the definition including this scaffolding.

Edge Cases and Surprises

What are acceptable edge cases either way?

  • When authors, practitioners, experts, observers, etc., disagree with each other about whether something fits the concept or not.
  • When the candidate manifestation is so confidential that what "consensus" there might be is not significant.
  • When the claims are interested, e.g. marketing hype by an industrial vendor, a mercenary consultant, or even an academic at a time when the concept is trendy enough it can lead to increased funding. Interested claims need not be false, but should tend to err on whichever side is in the interest of its author.
  • When the claims are made by fringe authors, sometimes even deservedly celebrated experts, who dissent on a given point with the rest of the mainstream. The expectation would be that these authors are likely cranks or at least wrong on that point, but they may be unsung geniuses with undeservedly ignored insights.
  • There can be plenty of honest mistakes even among experts in the field, and even more so among those who are only experts of a very narrow subfield, sometimes only of a single topic in the field (e.g. one programming language or software product). Disagreement is not an indictment, agreement is not an endorsement. Though often they go together.
  • Edge cases, including, sometimes unsuspectedly, widely assumed claims, can thus lead to smaller or bigger surprises, wherein some weaker or stronger expectation is falsified after careful observation.

Edge cases should be independently confirmed (or rejected) based on first principles. The greater the discrepancy between observation and expectation, the greater the surprise (greatest if the edge case was not suspected), and the more new information is brought by the edge case. This is a great opportunity to refine and test these “first principles” that underlie your definition. After resolving these edge cases one way or the other, and examining the underlying reasoning offered to explain those choices, does accepting or rejecting the claims at stake lead to a better or worse definition?

Differential Analysis

More generally, insight is usually gained at the margins:

  • It would be extremely surprising if there were absolutely no surprises, no mistakes, no difficult edge-case, and if a simple heuristic could adjudicate every case without resorting to clearer principles than most common definitions are expected to provide.
  • Looking at discrepancies between observations and expectations is exactly how better understanding is achieved compared to the raw expectations; how underlying structure can be discovered that was not visible or at least not obvious in the main of the concept.
  • Insight may be found in either explaining or explaining away the subtle differences between very similar presentations, or the not-so-subtle differences in very different ones.
  • Each time your understanding leads to adding or changing criteria to a concept definition, you should examine whether the new criteria contradict previous ones, or makes some of them redundant, or is redundant with them, or whether some criteria can be happily combined and simplified, etc. Each criterion in the final version should be simple and necessary. You may make several attempts and refine or correct them.
  • Once you have identified the distinction between adjacent concepts that many confuse, or the decisive criterion that allows you to solve an edge case, this is a great opportunity to further explore and understand the concept that lied at the discrepancy between understanding and misunderstanding, and map it.
  • If an edge-case potential manifestation of the concept differs from baseline expectations by its attributes leading it to be able to more than pass the conceptual criteria, then of course it should count as passing, and if the differences lead to not passing all criteria, then of course it should count as not passing.
  • Those unusual cases can help you refine what is the essence of the concept, by stripping its contingencies and going for the essentials.
  • The common case might only be the simplest/cheapest/earliest-found/most-popular case, and not the general case.
  • Keep exploring these cascading differences as long as you make progress, and that progress leads to notable improvement to your proposed answer.
  • It is better to define concepts with shorter names in terms of their more general case, and to have separate concept with a longer, qualified name for the special case.
  • Nevertheless, let's keep the target name for the target concept, even if we find that somehow it is "only" a special case of something else, that then deserves a different name.

Minimal Formal Models

Once you have a pretty good informal understanding of a concept, you can deepen your understanding by seeking a minimal formal model of said concept.

  • It is pointless and a waste of time to seek a formal understanding of a concept before you already have identified the concept and given it a satisfying informal definition.
  • However, once you have such a satisfying informal definition, a formal model can yield a lot of insight about things are actually equivalent, or analogous, and things that are very different (indeed belonging to incommensurable types or categories).
  • A formal model should use suitable logic that only as rich as strictly necessary to formalize the concepts at stake. You should handwave it at first to see what capabilities you actually need or don't need in the end (classical, linear, temporal, epistemic, probabilistic, etc.)
  • The computational side of a logic system is usually a variant of the lambda calculus, especially when discussing software. There isn't really a canonical "the" lambda-calculus, so pick the most appropriate variant carefully. With great power come great responsibility.
  • Look at the patterns of your λ-terms, the types that they correspond to; the constant combinators by which terms are combined or transformed: for terms without combinators are just a chaotic gas of non-interacting atoms; it's the interactions between those terms that are the structure of the domain. What are the kinds of terms at stake, the signatures of useful operators?
  • Can your presentation be reduced to a small kernel? Can you maximally eliminate redundancy, generalize your terms, etc.? If your simpler concepts are too complex, the complexity will percolate to larger concepts, so really try to simplify them away.
  • Simplest in the historical context of invention and/or simplest in common informal models is not quite the same simplest in the context of logic and formalism. Prefer the latter, it's more enlightening. Which presentation has the fewer concepts and/or rules?
  • Every kind of term and every combinator in your model must be defended: exhibit a case that dies without it, or else first try to exhibit it as a degenerate or special case of another, and eliminate it. (This is the same rule as for enumerations within definitions, applied to the model's own inventory.)
  • A model is not complete until you have checked, for each manifestation you classify, how each entity and operation of the model is realized there: directly denotable and computable by programs? present only at some earlier stage (design time, compile time, inside the implementation, in the programmer's head)? several model entities merged into a single realization? absent altogether? These realization patterns are data: recurring profiles may be concepts worth naming, and differences of profile may explain differences in power between manifestations.
  • Check whether the model applies to its own machinery: if the entities of your model are produced or transformed by something of the same shape one level up, the model should say so. Meta-level self-application of the model is a test of a model's closure, not a mere curiosity.
  • The proof of the pudding of a model is that it is executable—and better, already executed: look for systems in the wild, in any industry however remote from the concept's academic home, that implement your model nearly as-is. Search where the purpose is the daily job, not only where the concept's name is the daily word. Independent reinvention is confirmation.
  • Discrepancies between model and reality are gaps to account for, sometimes in the model, sometimes in the concept map itself.

Even Greater Definitions

Once you have precisely identified a concept, explored the surrounding conceptual map, and found a satisfyingly simple definition, your task is not over yet: you can try to relate the concept identified to its purpose, and give an even better definition in terms of the best way to achieve said purpose; or maybe the best way to achieve it given some constraint or strategy (assuming the concept indeed corresponds to the solution to a problem).

Why did anyone bother with the concept? What purpose does it serve? What problem did it solve? Did it improve some economic efficiency or biological fitness? How does that compare to the adjacent concepts from which you distinguished it? Was it the best, simplest or only solution to the problem? Can you conceptualize and appropriately narrow that problem such that the target concept is indeed the best solution? Does that lead to a simpler definition of the target concept? Ask also what the naive or previously available solution to that problem was, and what constraint made it unavailable, forbidden or insufficient: the content of a concept is often the exact shadow of that constraint. These problems and goals should necessarily be at a somewhat more abstract level than your precise definition, or they do not take the definition to to the next level.

In the case of concepts with an economic efficiency or biological fitness, etc., you may also climb the conceptual ladder one more rung, and look at the phenomenon and its effects from the point of view of the wider society or ecosystem. Often the details at the individual level also follow from a simpler more abstract definition at a higher level. But maybe not. Don't use a definition at a higher-level if it makes the explanations more complex. Each change of point of view has to earn its cost.

In pioneering for new concepts, the wherefore (what you want) precedes the why (how things fit together) that precedes the what (the solution offered). But in analyzing and defining an existing concept, the what (the concept you're trying to understand) precedes the why (how things fit together) that precedes the wherefore (what you want that it brings you).

Procedural Constraints

For the sake of this exercise, please iterate a loop wherein:

  1. You try your best to organize your ideas from memory (closed book); apply all the criteria below to make sure your answers or as good as you can make them; loop several passes in step 1 as long as you make notable progress.
  2. Print your status so far: your best answer(s), explanations, notable changes from previous pass, and dissatisfactions.
  3. If you think you need additional information to improve your answers, Do a search for bibliography, articles, keywords, taxonomy, etc. (open book) You may use agents or forks for gathering information and summarizing findings. Then go back to step 1 with your new findings.
  4. When you are satisfied that your answer is good enough (despite any minor disatisfaction on non-essential points), carefully check the answer against the success criteria, and against the test cases that you came up with. If it appears not so good after all, go back to step 1.
  5. Print your final answer. Stop.
@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 1 Response by Fable 5

Pass 1 (closed book)

Candidate definitions from memory, and what's wrong with each:

  1. The checklist: "encapsulation, inheritance, polymorphism (+ abstraction)." Symptom: enumeration without a criterion — it doesn't say which items are essential, why these four, or what "polymorphism" (of the several kinds) is meant. Haskell has abstraction and parametric polymorphism yet is agreed non-OO; VB6 had encapsulation and interface polymorphism yet was widely denied the title. Undershoots and overshoots at once.
  2. Kay's: messaging, local retention/protection/hiding of state-process, extreme late binding of all things. Deep, but as stated it covers Erlang (which its own author only half-jokingly called object-oriented) and, taken strictly ("only messaging"), excludes CLOS, whose generic functions dispatch on all arguments rather than sending to one receiver — yet CLOS is OO by unanimous consent, it's in the name. Partly an in-joke of the Smalltalk school.
  3. Wegner's ladder (object-based ⊂ class-based ⊂ object-oriented = objects + classes + inheritance). Clear and productive, but classes fail the necessity test: Self and pre-2015 JavaScript are prototype-based, classless, and universally counted as OO. Also "inheritance" is stated as a mechanism rather than as the capability it delivers.
  4. Cook's: OOP = programming with procedural data abstraction — an object is a value known only through an interface of operations, dual to an ADT which hides a representation behind a type. This is the sharpest single insight in the literature, but alone it admits Go and Rust trait objects as fully OO, which the public contests, and it deliberately brackets inheritance.

Emerging synthesis. Run the user's own rule of thumb — "does the language directly express the usual OO patterns and idioms?" — over the contested cases and see where the fault line falls. Strategy, Observer, Composite, State need only interfaces + runtime dispatch. Template Method, super calls, mixin layering, framework hot-spots need something more: a method defined in one place must, when it calls a sibling operation on itself, reach the overriding version supplied later. That property — late binding of self, a.k.a. open recursion — is exactly what Go embedding lacks (an embedded method never re-dispatches to the outer type), what Rust lacks, what VB6 lacked, and what Ada 95 and Fortran 2003 gained at the precise moment the lore started calling them OO. The contested cases sit exactly on this boundary. That is strong differential evidence.

Draft criteria (each named, each to be defended):

  • E — Encapsulated behavioral values (objects). First-class values coupling a hidden representation with the operations on it, usable by clients only through those operations (procedural data abstraction).
  • D — Dynamic dispatch. The code answering an operation request is selected at run time by the participating object(s), so any object satisfying an interface can stand wherever the interface is expected — clients stay closed while the set of representations stays open.
  • O — Open recursion. New behavior is definable as a delta on existing behavior (inheritance, delegation, mixins, method combination — mechanism irrelevant), with self-reference bound late so the delta takes effect throughout what it modifies.

Classes, prototypes, message syntax, single vs. multiple dispatch, static vs. dynamic typing: implementation choices, not the concept. Object identity looks derivable rather than primitive: once representation is hidden (E) and state may mutate, reference identity is the only coherent sameness left; immutable objects (Java's String) remain objects, so identity is a corollary, not a criterion.

Dissatisfactions after pass 1: (a) verify the Kay quote and the CLOS tension; (b) check whether any survey already did this decomposition (I recall a CACM study surveying definitions); (c) confirm the formal side — I believe the Abadi–Cardelli ς-calculus has exactly three primitives matching E/D/O, and Cook–Palsberg model inheritance as fixpoints of generators, which would make the definition's minimality checkable; (d) pin citations and DOIs.

Open book# Pass 2 (closed book, integrating findings)

The open-book round confirmed the map and sharpened three things.

The literature's state. Cook's OOPSLA 2009 essay argues that even after decades of research, the two forms of data abstraction — abstract data types and objects — remain widely confused, and his object concept (a value known only through its operations) is the cleanest foundation for criterion E. Armstrong's CACM survey derived the "essential elements" of OO by analyzing the existing literature, distilling eight recurring "quarks" (object, class, abstraction, encapsulation, inheritance, message passing, method, polymorphism). This is valuable as evidence — it shows the field converged by frequency-count on an enumeration, precisely the bad-definition symptom to overcome: her eight quarks mix essentials (encapsulation, dynamic method binding, inheritance-as-capability) with mechanisms (class, method, message) and with a term ("abstraction") too generic to delineate anything. Kay's first-hand definition — OOP is only "messaging, local retention and protection and hiding of state-process," plus extreme late binding of everything — maps almost term-for-term onto E (local retention/protection/hiding), D (messaging as late-bound operation selection), and O (late binding "of all things," including self). So the inventor of the term, the semanticist of inheritance (Cook), and the mechanism-agnostic analysis of inheritance by Black, Bruce and Noble in "The Essence of Inheritance" independently triangulate the same three-part core. Independent reinvention is confirmation.

Refinement 1 — dispatch belongs to the participating objects, not "the receiver." The CLOS edge case forces this generalization: generic functions select methods at run time on all arguments' classes, and CLOS is OO by universal consent. Single-receiver dispatch is the common special case, not the concept. This also quietly fixes Kay's formulation, which taken literally excludes CLOS. Multiple dispatch is strictly more expressive — by the brief's superset rule, it passes.

Refinement 2 — "directly supports" must itself be defined, or every Turing-complete language passes. A language directly supports a construct when the language provides the construct's machinery once and for all — its instances are denotable, composable values, usable without re-erecting the mechanism at each use site or performing a whole-program transformation. C programmers encode objects (structs of function pointers, self passed by hand, as in the Linux VFS or GObject); C does not directly support them, because the dispatch and self-binding machinery is rebuilt manually each time. This clause is what excludes tar-pits while letting the C encodings serve as evidence for the model (below).

Refinement 3 — identity is a corollary, not a criterion. Booch-style definitions list "identity, state, behavior." But once representation is hidden (E), behavioral equivalence is the only intrinsic sameness; languages with mutable encapsulated state must then supply reference identity as the practical stand-in. Immutable objects (Java's String) remain objects. Identity falls out of E plus mutability; adding it as a criterion is redundant.

Edge cases adjudicated

Go and Rust — E ✓ (methods, unexported fields, interface/trait abstraction), D ✓ (interface values, dyn Trait), O ✗: Go's embedding forwards calls without rebinding the outer receiver, so a promoted method never re-dispatches to an override; Rust has no override at all. Template Method, super-chains, mixin layering, framework hot-spot patterns lack direct expression. Verdict: object-based, not object-oriented — and gratifyingly, these are exactly the languages the public contests (the Go FAQ itself answers "yes and no"). The live fault line of public disagreement falls precisely on criterion O, which is strong evidence O is real and criterial rather than incidental.

VB6 — E, D via interfaces; no implementation inheritance, no late-bound self across a derivation: object-based. Matches the contemporaneous complaint that it wasn't "truly" OO. Ada 83 → Ada 95 and Fortran 90 → Fortran 2003: the added features (tagged type extension with overriding dispatched primitives; type-bound procedures with override) are exactly D and O — the lore's before/after judgment flips exactly when the criteria are met. JavaScript and Self: classless, but delegation gives O (this is late-bound through the prototype chain) — always counted OO, so classes are not essential; Wegner's 1987 ladder must be amended on this point. CLOS: D generalized, O via inheritance plus call-next-method; E supported (accessors, packages) though not enforced — but C++ friend and Java reflection also pierce encapsulation, so the criterion is direct support of abstraction, not unbreakability. OO. Erlang: encapsulated processes, run-time substitutability of any process behind a message protocol — E and D at process granularity, no O; also, receive is client-side case analysis inside the process. This is the actor model: an adjacent sibling concept, not a subset or superset — which explains why the Kay lineage calls it deeply object-oriented while mainstream taxonomy doesn't. Naming the sibling dissolves the dispute. Haskell / ML: the abstraction unit is the type or module, not the value; heterogeneous substitution and open recursion exist only as encodings (existentials, records of closures, hand-tied fixpoints). Per Reynolds and Cook these are the dual discipline — ADTs — and the consensus "not OO" verdict is principled, not prejudice: adding operations is cheap there, adding representations is what's cheap here.

Minimal formal model

Abadi and Cardelli's ς-calculus is the minimal executable model, and its inventory audits the definition. It has exactly three constructs: object formation [lᵢ = ς(xᵢ)bᵢ] (a record of methods, each closed over its own self-parameter — this is E: the only thing a body can touch is reached through self), method invocation o.l (the body run is the one o itself carries — this is D), and method override o.l ⇐ ς(x)b (producing a modified object whose other methods' self now refers to the modified whole — this is O). Drop override and you get the object-based fragment (the Go tier). Drop the self-binding and records of methods degenerate into plain records of functions — the ADT/closure world. Each construct has a case that dies without it; none is a special case of another. Equivalently, at the class level (Cook–Palsberg): a class is a generator G : Self → Record, an instance is fix(G), and inheritance is transformation of the generator before taking the fixed point — which is exactly why an override propagates into inherited code, and why "inheritance" without late-bound self (mere copying or forwarding) is not the same capability.

Realization profiles are data. In Smalltalk, Self, and JavaScript, all three ς-operations are run-time denotable; in C++/Java, override exists only at class-definition time — the class-based/prototype-based split is precisely at which stage the ς-override operation is available. The model closes over itself: in Smalltalk and CLOS, classes are themselves objects made by metaclass generators — the machinery is in the model's own image. And it runs in the wild where the problem lives rather than the word: COM's binary interfaces, Unix VFS file_operations tables, GObject — hand-built records of functions with explicit self, reinvented wherever independently compiled components must interoperate. Which points at the purpose.

The purpose-level definition

Why did anyone bother? The problem: extend a system with new kinds of interoperating things without editing the code that uses them. The naive solution — a closed tagged union plus functions that case-analyze it — dies under the constraint that clients ship separately and cannot be edited (libraries, frameworks, operating systems, plugins: Aldrich's observation that objects become inevitable exactly where independently developed parties must interoperate through a service abstraction). OOP is the shadow of that constraint: E packages each variant's behavior with its data so a variant is self-describing; D defers selection to run time so clients stay closed while the set of variants stays open; O makes the cost of the n-th variant proportional to its difference from existing ones. Reynolds saw in 1975 that this is one pole of a duality — procedural data abstraction makes new representations cheap and new operations dear, ADTs the reverse — later nicknamed the expression problem by Wadler. OOP is the discipline that chooses the representation-open pole.

Check against the success criteria and stop

Meaningful: E∧D∧O accepts Simula 67 (whose virtual is D and O born together, establishing priority), Smalltalk, C++ (in its virtual subset), Java, CLOS, Eiffel, Self, JavaScript, Python, Ruby, Ada 95, Fortran 2003, OCaml objects; rejects C, Pascal, Fortran 77, SML, Haskell, Prolog, SQL; and places every genuinely contested case (Go, Rust, VB6, Erlang) in a named adjacent tier rather than fudging a gradient — no wishy-washy membership, a ladder of crisp concepts. Clear: three named criteria, each defined via prior well-defined concepts. Consistent, and consistent-with-lore including the before/after flips. Productive: it predicts where objects get reinvented (interoperability boundaries) and explains the class/prototype split as a staging difference. Constructive: the ς/generator model is executable. Remaining minor dissatisfaction, acknowledged and accepted: whether O should be required rather than characteristic of full OO is where honest experts (Cook vs. Wegner's lineages) genuinely split; the taxonomy names the split instead of hiding it, which is the correct resolution of a real disagreement, not a failure to decide.

Final answer

Object. A first-class value that couples a (possibly hidden, possibly mutable) representation with the operations on it, such that clients can use it only through those operations — a procedural data abstraction. (Corollary: since the representation is inaccessible, sameness is behavioral; mutable objects therefore carry reference identity.)

A language is object-oriented iff it directly supports building programs out of objects with all three of:

  • E — Encapsulation. Objects as above are definable and usable, so a variant of behavior travels as one self-describing value.
  • D — Dynamic dispatch. The code answering an operation request is selected at run time by the participating object(s) themselves; hence any object honoring an interface can stand wherever that interface is expected, and clients remain closed while the set of implementations remains open.
  • O — Open recursion. New object behavior can be defined as a differential modification of existing behavior — by class inheritance, prototype delegation, mixins, or method combination, indifferently — with self-reference bound late, so that the modification takes effect throughout the behavior it modifies.

"Directly supports" means the language supplies the machinery (dispatch, self-binding, modification) once and for all, with objects as denotable, composable values — not merely that the machinery can be encoded.

A program is written in object-oriented style to the degree that its variation across representations is expressed by dispatch on objects rather than by client-side case analysis over a closed set of alternatives.

Taxonomy (each tier crisp): module/ADT languages (E's dual: type-hidden representation, static operation selection — ML, Ada 83, CLU); object-based languages (E+D — Go, Rust with trait objects, VB6, Emerald); object-oriented languages (E+D+O); pure object-oriented languages (all values are objects and all operations dispatch — Smalltalk, Self, Ruby nearly). Adjacent sibling: actor systems (E+D at process granularity, messaging asynchronous, no O — Erlang).

Purpose form (the same concept, one rung up). Object-oriented programming is data abstraction arranged for openness along the representation axis: the least discipline that lets a system acquire new kinds of interoperating components without modification of existing clients (E+D), at a cost per new kind proportional to its novelty (O). Where the dual constraint holds — a fixed set of representations but open-ended operations — its mirror image, the ADT discipline, is the right tool; mature languages support both, and the confusion between them is the root of most bad definitions of OOP.

@article{dahl1966simula,
  author={Dahl, Ole-Johan and Nygaard, Kristen},
  title={{SIMULA}---an {ALGOL}-based simulation language},
  journal={Communications of the ACM}, volume={9}, number={9}, pages={671--678}, year={1966},
  doi={10.1145/365813.365819},
  note={Priority for objects and classes; virtual procedures (criteria D and O) appear in Simula 67, cf. Dahl, Myhrhaug \& Nygaard, The SIMULA 67 Common Base Language, Norwegian Computing Center S-22, 1970}}

@incollection{reynolds1975user,
  author={Reynolds, John C.},
  title={User-defined types and procedural data structures as complementary approaches to data abstraction},
  booktitle={New Directions in Algorithmic Languages}, publisher={IFIP Working Group 2.1}, year={1975},
  note={Reprinted in Gunter \& Mitchell (eds.), Theoretical Aspects of Object-Oriented Programming, MIT Press, 1994. Earliest statement of the objects/ADT duality}}

@article{cardelli1985understanding,
  author={Cardelli, Luca and Wegner, Peter},
  title={On understanding types, data abstraction, and polymorphism},
  journal={ACM Computing Surveys}, volume={17}, number={4}, pages={471--523}, year={1985},
  doi={10.1145/6041.6042}}

@inproceedings{wegner1987dimensions,
  author={Wegner, Peter},
  title={Dimensions of object-based language design},
  booktitle={OOPSLA '87}, pages={168--182}, year={1987},
  doi={10.1145/38765.38823},
  note={Source of the object-based/class-based/object-oriented ladder, amended here: classes are inessential}}

@inproceedings{cook1989denotational,
  author={Cook, William R. and Palsberg, Jens},
  title={A denotational semantics of inheritance and its correctness},
  booktitle={OOPSLA '89}, pages={433--443}, year={1989},
  note={Classes as generators; instances as fixed points; inheritance as generator transformation --- the semantics of criterion O}}

@incollection{cook1990oopvsadt,
  author={Cook, William R.},
  title={Object-oriented programming versus abstract data types},
  booktitle={Foundations of Object-Oriented Languages (REX Workshop)},
  series={Lecture Notes in Computer Science}, volume={489}, pages={151--178}, publisher={Springer}, year={1991},
  doi={10.1007/BFb0019443}}

@inproceedings{kay1993early,
  author={Kay, Alan C.},
  title={The early history of {Smalltalk}},
  booktitle={HOPL-II}, pages={69--95}, year={1993},
  doi={10.1145/154766.155364}}

@book{abadi1996theory,
  author={Abadi, Mart\'in and Cardelli, Luca},
  title={A Theory of Objects}, publisher={Springer}, year={1996},
  doi={10.1007/978-1-4419-8598-9},
  note={The $\varsigma$-calculus: formation, invocation, override --- the minimal formal model matching E, D, O}}

@misc{taivalsaari1996notion,
  author={Taivalsaari, Antero},
  title={On the notion of inheritance},
  howpublished={ACM Computing Surveys 28(3), pages 438--479}, year={1996},
  doi={10.1145/243439.243441},
  note={Survey justifying the mechanism-agnostic reading of criterion O}}

@misc{wadler1998expression,
  author={Wadler, Philip},
  title={The expression problem},
  howpublished={Email to the java-genericity mailing list}, year={1998},
  url={https://homepages.inf.ed.ac.uk/wadler/papers/expression/expression.txt}}

@misc{ram2003kay,
  author={Kay, Alan C. and Ram, Stefan},
  title={Dr.\ Alan Kay on the meaning of ``object-oriented programming''},
  howpublished={E-mail exchange, published by Stefan Ram}, year={2003},
  url={https://www.purl.org/stefan_ram/pub/doc_kay_oop_en}}

@article{armstrong2006quarks,
  author={Armstrong, Deborah J.},
  title={The quarks of object-oriented development},
  journal={Communications of the ACM}, volume={49}, number={2}, pages={123--128}, year={2006},
  doi={10.1145/1113034.1113040},
  note={Frequency-count survey of definitions; evidence of the enumeration problem this definition resolves}}

@inproceedings{cook2009understanding,
  author={Cook, William R.},
  title={On understanding data abstraction, revisited},
  booktitle={OOPSLA '09}, pages={557--572}, year={2009},
  doi={10.1145/1640089.1640133}}

@inproceedings{aldrich2013interoperability,
  author={Aldrich, Jonathan},
  title={The power of interoperability: why objects are inevitable},
  booktitle={Onward!\ 2013}, pages={101--116}, year={2013},
  doi={10.1145/2509578.2514738},
  note={The purpose-level account: dynamic dispatch as the enabler of interoperation among independently developed components}}

@misc{black2016essence,
  author={Black, Andrew P. and Bruce, Kim B. and Noble, James},
  title={The essence of inheritance},
  howpublished={arXiv:1601.02059}, year={2016},
  url={https://arxiv.org/abs/1601.02059}}

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 2 Prompt

Excellent Round 1 response, but you can do better:

  • You seem to mention discussions from webpages, that may also deserve citing.
  • I insist you examine every clause of every definition you give, and decide if it's really redundant given the other clauses: will it exclude manifestations that fit the concept, or include manifestations that don't?
  • similarly for every word of every clause you keep.
  • such definitional edits may lead to slightly different concepts, but that's OK as long as the target concept fits and is simpler. Not OK if the target concept suddenly covers a very different set of manifestations.
  • I insist you examine minimal formal models, as they may inform your minimal definition.
  • You are right to accept this variety of manifestations, but you may need to better understand how the salient features of these manifestations, that make them fit the concept, how they relate to each other, if some are more general than the others, or can be generalized, etc.
  • To access the essence of the concept, reach of a simpler definition that may have richer stories for how things fit, rather than a richer definition with simple stories for fitting.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 2 Response by Fable 5

Pass 3 (closed book): clause-by-clause and word-by-word audit

Round 1 ended with three criteria (E, D, O). Taking each clause seriously as a candidate for deletion or derivation:

Auditing E (encapsulation). The clause bundled four claims: objects are first-class values; they couple state with behavior; the representation is hidden; use is only through operations. Test each against consensus manifestations. Hiddenness as enforcement is false of JavaScript (all properties open), Python (conventional privacy), CLOS (slot-value) — all indisputably OO; so enforcement was never criterial. State is not criterial: immutable objects (String) and stateless strategy objects are objects. First-classness is not independent: D already demands that any conforming object can stand at a use site, and standing-in requires flowing there as a value — first-classness is entailed. And the remaining content — clients depend on interface, not representation — is a theorem of D: a client that interacts with a value only by issuing requests whose implementations the value itself supplies has no channel through which representation could be observed. In Cook's terms, once you say "interface-only use," the hiding has already happened. Verdict: E is deleted as a criterion and re-proven as a corollary. Extension check: nothing new enters, because "the value supplies the implementation" (D) is exactly what makes interface-only use directly expressible; nothing lacking that acquired E-hood indirectly.

Auditing D word by word. "At run time" — as opposed to what? Compile-time selection (overloading, C++ templates, monomorphized type classes). But "run time" is the wrong concept: under whole-program devirtualization a Java call may be resolved at compile time without the program ceasing to be OO. The criterial content is whose information selects: the implementation of a request is determined by the value(s) addressed, not by static knowledge at the request site. Run-time selection is then a corollary (whenever the value is not statically known, selection cannot happen earlier), robust under staging. "The receiver" — falsified by CLOS, whose generic functions select on all arguments; generalize to the participating value(s); single-receiver is the common special case. "Message" — carries actor connotations (asynchrony, copying) that are contingent; "request naming an operation" is the neutral general term. This also repairs Kay's formulation, which read literally excludes CLOS and includes Erlang.

Auditing O. The clause contained two things: (a) modifications propagate into inherited behavior (late-bound self), and (b) a derivation mechanism (define new behavior by difference). Are both primitive? (a) turns out to be D again: it says an object's requests to itself are resolved by the same value-supplied selection as anyone else's requests — dispatch made total, exempting no request, Kay's late binding "of all things" made precise. So (a) is absorbed into D as the single word every. (b) is not derivable: a language could dispatch totally yet offer no way to produce a modified object (ς-calculus minus override — the drop-one argument stands). But note the entanglement, which Round 1 missed: without derivation, totality is unobservable (if nothing can ever differ from its origin, early- and late-bound self coincide — this is why the question never arose for VB6); and without totality, derivation degenerates to copying/forwarding (Go embedding), which fails the differential patterns. Each clause gives the other its observable content. Two clauses, then, and they are two halves of one thing — which the formal model now exhibits.

Pass 4: the minimal models, made to inform the definition

ς-calculus. Beyond object formation, it has exactly two operations: invocation and override — matching the two surviving clauses one-for-one, with the ς-binder (each method abstracted over self) supplying totality. Encapsulation appears in the model as it now does in the definition: as a theorem — contexts can only invoke and override, so observational equivalence of objects is behavioral equivalence.

The generator/fixpoint model — and the sharpest formulation found in this whole exercise. Following Cook–Palsberg: behavior with self-reference is a generator G : Self → Behavior; an object is fix(G), the knot of self-reference tied. Then: use goes through the fixpoint; object-oriented reuse goes through the generator. Dispatch = lookup in fix(G). Derivation = a transformation G ↦ G′ applied before the knot is tied, so fix(G′) re-resolves every self-reference against the modified whole — propagation is not stipulated, it is what taking the fixpoint last means. Every mechanism in the zoo is now one thing seen at different stages: class inheritance transforms named, reified generators (and Featherweight Java, the minimal class-based calculus, needs visibly more machinery than ς — evidence that classes are derived apparatus, generators made into declarations, not part of the essence); mixins are composable generator-transformers (Bracha–Cook); CLOS method combination is an algebra of generator layers, call-next-method walking the pre-fix chain; prototype delegation computes the fixpoint lazily, per request, by walking the chain — the class/prototype split is merely the evaluation strategy of fix. And the excluded cases are now positively characterized rather than merely rejected: Go embedding and Rust composition are post-fixpoint reuse — they compose already-tied objects, so nothing re-resolves; Erlang/OTP behaviours are one fixed layer of generator parameterization (the generic server calling back a supplied module — Template Method frozen at depth one) rather than a derivation algebra. This is the "richer story" the simpler definition buys: one mechanism, one staging distinction, and the entire taxonomy of manifestations falls out as realization profiles of fix.

The duality, formally grounded. The opposite pole also has a one-line model: Mitchell and Plotkin showed that abstract data types are existential types — a hidden representation type packaged with operations over it, in second-order lambda calculus. Objects are the recursive/coinductive record answer to the same question. Reynolds's 1975 complementarity is thus not an observation about style but a duality of quantifiers, which is why no amount of feature-addition turns ML modules into objects or vice versa: they answer "what varies freely?" differently (the operations vs. the representations).

Confronting the webpages that shaped the argument (now cited, as they deserve): Jonathan Rees's much-circulated letter argues "object-oriented" is a cluster of nine independent features that languages mix and match, so the term has no essence. The audit above is the direct answer: the cluster is real at the feature level, but it factors — two criterial clauses, everything else (encapsulation, subtype polymorphism, identity, classes, messages, protection) derived, optional, or a realization profile. A cluster concept is what an essence looks like before the corollaries have been separated from the criteria. Hillel Wayne's historical essay documents that objects, classes, and messages arrived together from Simula into Smalltalk and that "messaging is the only big idea" is partly retrospective; this justifies treating Kay's 2003 definition as one triangulation point among several rather than as the authority — while noting that the audit vindicates his "extreme late binding of all things" as the precise content of totality. The Go FAQ's own "yes and no" answer, read against this definition, decomposes exactly: yes to value-supplied dispatch, no to pre-fixpoint derivation.

Extension re-check (the constraint that edits must not move the concept). Simula 67, Smalltalk, C++ (virtual subset), Java, Eiffel, CLOS, Self, JavaScript, Python, Ruby, Perl 5, OCaml objects, Ada 95, Fortran 2003: both clauses ✓, unchanged. C, Pascal, SML, Haskell, Prolog: excluded as before (encodings, not direct support; or the existential pole). Go, Rust, VB6, Emerald: object-based tier, unchanged. Erlang: actor sibling, unchanged. The definition got smaller; the boundary did not move.

Final answer (v2)

Object. A value that answers requests — invocations of named operations — with implementations that it itself supplies.

(Word audit: "value" — must flow to generic use sites, so first-classness is built in, not appended. "Requests… named operations" — the neutral general term; "message" is the actor-flavored special case. "It itself supplies" — the name-to-implementation mapping is the value's property, not the request site's: this one phrase is the whole of dispatch and the whole of the objects/ADT distinction, since in an ADT the site's module supplies the implementation.)

A language is object-oriented iff it directly supports both:

  1. Total dispatch. The implementation answering every request is supplied by the participating value(s) — including requests an object's own behavior makes of itself. ("Every" is load-bearing: delete it and Go passes; it is late binding of self, made a word instead of a clause. "Participating value(s)": single-receiver dispatch is the common case, CLOS the general one. "Supplied by the value, not the site" makes run-time selection a corollary and survives devirtualization.)
  2. Derivation. New behavior is definable by its difference — added or replaced operations — from existing behavior, the modification acting before self-reference is resolved. (No propagation rider: given clause 1, propagation is a theorem. "Before self-reference is resolved" is the pre-fixpoint condition that separates inheritance, delegation, mixins, and method combination — all of which pass — from copying, forwarding, and embedding, which act after.)

"Directly supports": the language provides this machinery once and for all, with objects as denotable values — encodings that rebuild dispatch or self-binding per use (C's function-pointer structs) do not qualify, though their spontaneous appearance at interoperability boundaries (VFS, GObject, COM) confirms the model.

Corollaries, not criteria: encapsulation (a requests-only client cannot observe representation); substitutability and subtype polymorphism (request sites are generic over suppliers); propagation of overrides (totality applied to self-requests); object identity (hidden mutable representation leaves reference identity as the only non-behavioral sameness); classes (reified, named generators — one realization among several).

Essence form. Self-referential behavior is a generator; an object is its fixpoint. Object-oriented programming is programming where use goes through the fixpoint and reuse goes through the generator. The dual discipline (ADTs, existentially typed) fixes the representation-suppliers and leaves operations open; OOP fixes the operation-names and leaves suppliers open — choose by which axis of the expression problem your constraints leave open.

Purpose form (unchanged from Round 1, now resting on fewer primitives): the least discipline by which a system acquires new kinds of interoperating components without modification of existing clients (clause 1), at a cost per kind proportional to its novelty (clause 2).

Taxonomy: module/ADT languages (existential pole: ML, CLU, Ada 83) — object-based languages (clause 1 minus totality of self, no clause 2: Go, Rust, VB6, Emerald) — object-oriented languages (both clauses) — pure OO (all values objects, all operations requests: Smalltalk, Self) — sibling: actor systems (encapsulated processes, asynchronous requests, no derivation algebra: Erlang).

% --- additions and replacements to the Round 1 bibliography ---

@inproceedings{mitchell1985abstract,
  author={Mitchell, John C. and Plotkin, Gordon D.},
  title={Abstract types have existential types},
  booktitle={POPL '85}, pages={37--51}, year={1985},
  doi={10.1145/318593.318606},
  note={Earlier variant of the TOPLAS 10(3) 1988 version; the formal ground of the ADT pole of Reynolds's duality}}

@inproceedings{igarashi1999featherweight,
  author={Igarashi, Atsushi and Pierce, Benjamin C. and Wadler, Philip},
  title={Featherweight {Java}: a minimal core calculus for {Java} and {GJ}},
  booktitle={OOPSLA '99}, year={1999},
  note={Journal version: ACM TOPLAS 23(3):396--450, 2001. Minimal class-based calculus; its extra machinery relative to the sigma-calculus shows classes are derived, not primitive}}

@misc{rees2003cluster,
  author={Rees, Jonathan},
  title={On object-oriented programming (letter to Paul Graham)},
  year={2003},
  url={https://www.paulgraham.com/reesoo.html},
  note={The cluster-concept objection: nine features mixed and matched; answered here by factoring the cluster into two criteria plus corollaries}}

@misc{wayne2019kay,
  author={Wayne, Hillel},
  title={Alan {Kay} did not invent objects},
  year={2019},
  url={https://hillelwayne.com/post/alan-kay/},
  note={Historical corrective: objects, classes, and messages arrived together from Simula; messaging-primacy is partly retrospective}}

@misc{gofaq,
  author={{The Go Project}},
  title={Frequently asked questions: Is {Go} an object-oriented language?},
  url={https://go.dev/doc/faq#Is_Go_an_object-oriented_language},
  note={First-party ``yes and no''; decomposed by the present definition into yes-dispatch, no-derivation}}

@inproceedings{bracha1990mixin,
  author={Bracha, Gilad and Cook, William R.},
  title={Mixin-based inheritance},
  booktitle={OOPSLA/ECOOP '90}, pages={303--311}, year={1990},
  doi={10.1145/97945.97982},
  note={Mixins as composable generator transformations; evidence for the pre-fixpoint reading of derivation}}

The two remaining dissatisfactions, recorded honestly: (i) clause 2's "added or replaced operations" is still a two-case enumeration, though an exhaustive one (a name is either new or already bound), and I found no generalization that doesn't smuggle the enumeration into another word; (ii) whether totality should admit degrees (C++ dispatches only where declared virtual) — resolved here by holding languages to direct support rather than universal enforcement, at the acknowledged cost that "C++ is OO in its virtual subset" must be said out loud. Both are prices paid knowingly, at the right places: on real cases, not hidden in vague words.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 3 Prompt

Even better. But I think you go further:

  • The clauses and words you delete as you simplify may end up unnecessary, yet came in for a reason.
  • The clauses especially may have been historically added because they are the shadow or the higher purpose why the concept is used. This higher purpose deserves to be identified, named, defined. It may or may not be the whole story (the removed clause wasn't the whole story), but may be essential to it.
  • The words especially may correspond to common modalities of manifestation of the concept. They can suggest other modalities that you should examine. They can help identify the essence of the concept, make it simpler, while moving the richness to the modalities by which manifestations fit the concept.
  • Minimal formal models invite a search for quintessential manifestations of the concept, that closely fit these minimal models. Do they exist? Have they been attempted? What subtle or not-so-subtle failures of the model do they reveal by what they had to add, delete or modify compared to the model? This can also help you update your model and your definition, and make sure they are both complete and minimal.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 3 Response by Fable 5

Pass 5 (closed book): why every deleted clause and word came in — nothing removed without its reason recovered

The Round 2 simplification deleted material. The instruction is right: each deletion was load-bearing somewhere, just not in the delineation. So here is the full ledger — for each removed item, the purpose or modality it was the shadow of, now identified, named, and defined in its own right.

"Encapsulation" / "hidden" (the deleted clause E). It entered every historical definition because it names the purpose of the whole enterprise, dressed as a mechanism: Parnas's information hiding — decompose a system so that each module hides a decision likely to change, so that change stays local. Define the purpose-level concept properly: a client is representation-independent when its correctness depends only on the request protocol it uses, and on no property of the supplier beyond that protocol. Now the whole map snaps into place one level up: both poles of Reynolds's duality are disciplines of representation independence. ADTs achieve it in a closed world (the set of representations is fixed where the abstraction is defined; the type seals it); objects achieve it in an open world (suppliers unknown, unwritten, or untrusted when the client is written — hence selection must be deferred to the values themselves). So E was not wrong, and not merely a corollary either: it was the genus, of which OOP is the open-world species. The definition didn't need the clause; the concept's genealogy did. And E concealed a second thing the mechanism-level definition deliberately cannot supply: the normative half. Clause 1 says any value determining implementations can stand at a use site; it does not say when it should. That is behavioral conformance — Liskov's substitution principle, contracts on protocols — the semantic complement to the syntactic mechanism, and the reason "polymorphism" kept appearing in folk definitions with an oddly moral flavor. Mechanism (this definition) and norm (Liskov/Meyer contracts) are different concepts; conflating them is one of the classic confusions the ledger dissolves.

"Message." Kay's word, and it entered for a reason larger than dispatch: the cell/whole-computer vision — composition of autonomous parts that share nothing and interact only by requests. That purpose matured into its own concept (the actor model, distributed objects), and the word names a modality axis: request transport — from synchronous in-stack call, through asynchronous message with isolated failure, to remote invocation via proxies (the Proxy pattern being dispatch's gift to distribution: a stand-in can stand in). Erlang is no longer an awkward adjacency ruled on by fiat; it is clause-1 objects at the far isolation end of the transport axis, lacking clause 2.

"At run time." It entered because the open world is not only open in who but in when: plugins loaded after shipment, classes loaded into a running JVM, methods redefined in a live Smalltalk image, Erlang hot code upgrade. The word names the binding-time axis: compile-time (devirtualized), load-time, per-request, and — the endpoint the folk word "late binding" was always gesturing at — re-bindable during execution. Kay's "extreme" late binding is this axis pushed maximal; the definition requires only that binding be by the values, and the axis holds the rest of the richness.

"Receiver." The common realization, whose deletion (for CLOS) opens an axis: selection basis — single receiver, multiple dispatch, and at the known endpoint predicate dispatch (Ernst–Kaplan–Chambers), where implementations are guarded by arbitrary conditions on the participating values. Examining the endpoint improves a word: "supplied by the value" strains at predicate dispatch (the predicates live in the generic function), but "determined by the participating value(s), not by the request site" holds along the whole axis while still excluding type classes (determined by the site's static typing). One word changes in the final definition; the axis earned it.

"State" (deleted from E). It entered from the birth-purpose: Simula's objects were simulation entities with lifecycle and persistent local condition. Its content is the substrate axis: what the behavior closes over — nothing (stateless strategies), immutable data, mutable store, a whole process, or an external resource. The last point is worth savoring: the Unix file descriptor — an opaque handle answering a fixed protocol (read/write/close) whose implementation is determined per-value by the kernel's dispatch table — is a quintessential object living where nobody says the word.

"Class" (deleted from Wegner's definition in Round 1). Its reason surfaces only in Pass 6's audit, but state it here: classes entered the definitions as the shadow of behavior sharing — one generator, many fixpoints, and a single point of update so that changing behavior once changes it everywhere. Sharing is the invariant purpose; the class is one realization, delegation another (Lieberman showed prototypes achieve shared behavior without classes years before Self matured the idea). Not criterial for OO-hood; criterial for OO at scale.

Pass 6: quintessential manifestations of the minimal models — and what their deltas teach

The models predict that systems built as the model should exist. They do, and each one's deviations are data.

Self versus the ς-calculus. Self is the nearest living manifestation: objects, slots, requests, no classes. But it did not realize ς's functional override, which copies; Self added shared parent slots (delegation). The delta exposes a genuine gap in ς as a model of practice: the calculus has no account of many objects sharing one behavior, hence no economy of space and no single point of update. The model must therefore reify generators as shareable, mutable entities — which is what parent objects, classes, and traits all are. The deleted word "class" thus returns, not as a criterion, but as the model's repaired inventory item: the shared generator.

Smalltalk's super versus object-level override. super cannot be expressed with objects alone: it names a position in the pre-fixpoint chain of generators, invisible from the tied knot. Its very existence in the flagship OO language adjudicates between the two models: the ς-calculus is minimal for use; the generator/fixpoint model (Cook–Palsberg) is the minimal complete model, because reuse constructs (super, call-next-method, mixin linearization) all address the generator stack. The definition already speaks generator language ("before self-reference is resolved") — the quintessential manifestations confirm that choice was forced, not stylistic.

Obliq versus the ς-calculus. Cardelli built the calculus's own language — lexically scoped, distributed, object-based. Distribution immediately forced what the calculus is silent on: identity, location, migration, and aliasing across sites. Lesson recorded: identity is a corollary at the definition's level, but the transport axis promotes it to a first-class design problem — a corollary is not the same as a triviality, and the model should flag which corollaries become load-bearing under which modalities.

COM and the fragile base class — the strongest test the definition has faced. COM is objects reduced to the binary essence of clause 1 (interface tables, per-value dispatch, QueryInterface), and it deliberately prohibited implementation inheritance across component boundaries, offering post-fixpoint aggregation instead. Mikhajlov and Sekerinski later formalized why: the fragile base class problem. The diagnosis in this round's vocabulary: clause 2's power and its fragility are the same phenomenon — the open self is a semantic channel coupling derivative to origin, so an "innocent" revision of the base re-resolves through every descendant's overrides. Name the trade-off: open self versus sealed self. This does not amend the definition; it completes the story the definition tells about its own boundary tier. Go, Rust, COM, and the microservice orthodoxy ("composition over inheritance") are not failed or ignorant OO — they are the recurring, principled retreat to sealed-self composition wherever derivation would cross a trust, version, or team boundary. Clause 2, in mature practice, is scoped: exercised within a boundary of co-evolution, renounced across it. The contested cases were contested because they sit on a genuine engineering trade-off, and a good definition should predict exactly where the fights break out. It does.

Featherweight Java. The minimal class-based calculus deleted mutable state entirely and lost nothing definitional — independent confirmation that state belongs to the substrate axis, not the essence. What it could not delete: casts — the escape hatch from dispatch back to case analysis. Every industrial language keeps such a bridge (instanceof, downcasts, pattern matching), confirming Cook's observation that real languages are hybrids of the two poles, and adding a final modality: hybridization — where in the program each pole is deployed.

Final answer (v3)

Object. A value that answers requests — invocations of named operations — with implementations determined by the value itself.

A language is object-oriented iff it directly supports both:

  1. Total dispatch. The implementation answering every request — including requests an object's behavior makes of itself — is determined by the participating value(s), not by the request site.
  2. Derivation. New behavior is definable by its difference from existing behavior, the modification acting before self-reference is resolved.

(Sole change from v2: "determined by," which survives the selection-basis axis out to predicate dispatch while still excluding site-determined selection.)

Corollaries: encapsulation, substitutability, override propagation, identity, classes-as-shared-generators — as before, with the new annotation that corollaries can become load-bearing under specific modalities (identity under distribution; sharing at scale).

The purpose hierarchy, each level named: information hiding (Parnas: localize what changes) → representation independence in an open world (this concept; the closed-world solution being its dual, the ADT) → interoperation of independently developed and independently evolving parts (Aldrich) → bounded by the open-self/sealed-self trade-off (derivation's propagation is also its fragility; scope clause 2 inside boundaries of co-evolution).

The modality axes, where the richness lives (the essence stays two clauses; manifestations differ by position on): binding time (compile → load → per-request → live-rebindable); selection basis (single receiver → multiple → predicate); request transport (call → asynchronous message → remote proxy); protection strength (convention → scope → access control → capability → process isolation); substrate (stateless → immutable → mutable → process → external resource); derivation staging (eager fixpoint/classes → lazy fixpoint/delegation → run-time override); sharing (copied → shared generators); hybridization (where the ADT pole is bridged in). Every historical definition on record is this essence plus a frozen choice of modalities mistaken for criteria — which is why the definitions never quite overlapped, and why the cluster (Rees) looked like a cluster.

Essence form, unchanged and now stress-tested: an object is the fixpoint of a behavior generator; use goes through the fixpoint, reuse goes through the generator — with the addendum earned in Pass 6 that generators must be shareable (the truth inside "class") and that reuse through the generator couples what it empowers (the truth inside the industry's retreats).

% --- additions to the cumulative bibliography ---

@article{parnas1972criteria,
  author={Parnas, David L.},
  title={On the criteria to be used in decomposing systems into modules},
  journal={Communications of the ACM}, volume={15}, number={12}, pages={1053--1058}, year={1972},
  doi={10.1145/361598.361623},
  note={Information hiding: the purpose-level concept of which encapsulation clauses were the shadow}}

@inproceedings{liskov1987data,
  author={Liskov, Barbara},
  title={Data abstraction and hierarchy},
  booktitle={OOPSLA '87 Addendum}, year={1987},
  note={Earlier variant; journal treatment: Liskov \& Wing, A behavioral notion of subtyping, ACM TOPLAS 16(6):1811--1841, 1994, doi 10.1145/197320.197383. The normative complement (conformance) to the mechanism-level definition}}

@inproceedings{lieberman1986prototypes,
  author={Lieberman, Henry},
  title={Using prototypical objects to implement shared behavior in object-oriented systems},
  booktitle={OOPSLA '86}, pages={214--223}, year={1986},
  note={Priority for delegation; behavior sharing without classes, establishing sharing as the purpose and class as one realization}}

@inproceedings{ungar1987self,
  author={Ungar, David and Smith, Randall B.},
  title={Self: the power of simplicity},
  booktitle={OOPSLA '87}, pages={227--241}, year={1987},
  doi={10.1145/38765.38828},
  note={Quintessential manifestation of the object model; its shared parent slots expose the sharing gap in the pure sigma-calculus}}

@article{cardelli1995obliq,
  author={Cardelli, Luca},
  title={A language with distributed scope},
  journal={Computing Systems}, volume={8}, number={1}, pages={27--59}, year={1995},
  note={Obliq: the object calculus made a language; distribution forces identity, location, and migration semantics on which the calculus is silent}}

@inproceedings{mikhajlov1998fragile,
  author={Mikhajlov, Leonid and Sekerinski, Emil},
  title={A study of the fragile base class problem},
  booktitle={ECOOP '98}, series={Lecture Notes in Computer Science}, volume={1445}, pages={355--382}, publisher={Springer}, year={1998},
  note={Formalizes the open-self coupling: derivation's propagation and its fragility are the same phenomenon}}

@inproceedings{ernst1998predicate,
  author={Ernst, Michael and Kaplan, Craig and Chambers, Craig},
  title={Predicate dispatching: a unified theory of dispatch},
  booktitle={ECOOP '98}, series={Lecture Notes in Computer Science}, volume={1445}, publisher={Springer}, year={1998},
  note={Endpoint of the selection-basis axis; motivates ``determined by'' over ``supplied by''}}

@phdthesis{miller2006robust,
  author={Miller, Mark S.},
  title={Robust composition: towards a unified approach to access control and concurrency control},
  school={Johns Hopkins University}, year={2006},
  note={Object-capability discipline: the protection axis pushed to its endpoint, encapsulation as the security model}}

Status and remaining dissatisfaction. The definition itself has now been stable for one full pass under adversarial audit — every proposed change since v2 either bounced (extension would move) or reduced to one word ("determined"). What changed in this round is that the deleted material is fully accounted for: two purposes named and defined (information hiding / representation independence; behavior sharing), one normative complement identified (conformance), one trade-off named (open vs. sealed self), and eight modality axes absorbing what previous definitions froze into criteria. The one dissatisfaction I cannot discharge: the generator model still treats "modification of behavior" (clause 2's difference operator) as primitive rather than deriving which difference algebras are canonical — override, extension, linearized composition — from the purpose the way the rest was derived. I suspect there is a theorem there (something like: the difference operators closed under the sharing and conformance requirements are exactly the generator-wrapper compositions), but proving it would be a research contribution, not a definition, and the definition is complete without it. Stop.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 4 Prompt

First "simple" for many concrete people means they have (often rightfully) removed something unnecessary from the common assumptions, but seldom that they have reached the simplest concept. Meanwhile, simple for the more abstract people often means they have something very simple, but that tends to not completely model the target concept. There is something to learn from both, but great care should be taken to reconcile the two. Taking only one into account will lead you to miss a great deal. Look for actual manifestations that tried to directly apply the minimal model... and which formal model is truly minimal might be revealed when manifestations try to embody them.

Then, are you sure your clauses are all strictly necessary? Does removing one still identify the same manifestations? Then, maybe it was not necessary at one level even though it wasn't deduced from the other at that level... but maybe from a principle at a higher-level.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 4 Response by Fable 5

Pass 7: the two minimalisms, and the embodiment test that adjudicates between them

Line up the two families of simplifiers and let each judge the other.

The concrete minimalists — practitioners who removed a standard assumption and shipped a working system:

  • Self removed classes; delegation through shared parent slots sufficed.
  • JavaScript and Lua removed even objects-as-a-kind: Lua has only tables plus one fallback hook (__index), JavaScript only property maps plus a prototype link — and full OOP is recovered as a pattern over lookup-with-fallback.
  • Emerald removed inheritance and compile-time classes for the sake of distribution, keeping objects and structural conformity — and found that sufficient for its purpose.
  • E and the object-capability discipline removed inheritance deliberately, on security grounds, building objects as records of closures.
  • SICP-style Scheme removed everything except lambda: an object is a closure that dispatches on a request symbol — the folklore koan (the "Qc Na" story: closures and objects each the poor man's other) marks how far this goes.

The abstract minimalists — theorists whose calculi are tiny but do not completely model the target:

  • The ς-calculus's override is functional — it copies. No embodiment implements it that way: every real system needed sharing (one behavior, many objects, one point of update). ς's copying override is dead weight the concrete side exposes; its missing sharing is the gap Self's parent slots and every class system fill.
  • The pure messaging model got its direct embodiment test very early: Smalltalk-72, where each object literally parsed its incoming message stream — messaging maximal, structure minimal. It failed under its own purity — unreadable, unoptimizable, and with no principled derivation — and Ingalls's Smalltalk-76 retreated to compiled selectors, method dictionaries, and a superclass chain. The attempt to embody the abstract-minimal model is precisely what revealed it was below the concept: the concept required reified, chained behavior tables. Meanwhile the pure-message line matured into a different concept — Hewitt's actors, whose purpose is concurrency — confirming that fork rather than embarrassing it.
  • Featherweight Java is minimal only relative to class-based typing; its casts and its classes are machinery the untyped embodiments show to be projection and pattern, respectively.

The reconciliation. Ask what the concrete minimalists converge on when you strip their syntax, and what remains of the abstract models when you delete what no embodiment implements and add what every embodiment needed. Both sides land on the same small thing, which I'll call the chain model:

A behavior is an ordered chain of fragments (name → implementation tables). A request on a value looks the operation up along the value's chain; the implementation found executes with self bound to the whole value, not to the fragment where the lookup succeeded. New behavior is made by extending a chain in front of an existing one; chain tails are shared.

Check it in both directions. It is what Lua and JavaScript directly are (the __index chain, the prototype chain), so it passes the concrete test with almost no residue. It is the generator-stack of Cook–Palsberg with the fixpoint evaluated lazily per request, so it is abstractly complete: classes are named chains built eagerly; super and call-next-method are the chain successor; CLOS class-precedence lists are the chain made explicit; mixin systems and multiple inheritance are chain-construction algebras, with linearization algorithms (C3 and kin) as their normal forms — which also discharges most of Round 3's leftover dissatisfaction: the "canonical difference operators" I couldn't derive are exactly the operations that construct well-formed chains (prepend, compose, linearize); ς-override is the degenerate copying case; Smalltalk-76's method dictionaries plus superclass pointer are the chain's first industrial embodiment, arrived at by the failure of the model below it. And the residue analysis is itself informative: the one thing Lua could not get from lookup alone was self-binding (hence the : sugar passing the receiver), and the one thing JavaScript is notorious for getting subtle is this. The irreducible primitive, the thing embodiments must build in rather than pattern up, is the second half of the chain model's sentence: lookup finds the fragment; self remains the whole. That is open recursion, totality, and "late binding of all things" compressed to eight words, and it is exactly where every embodiment either paid the primitive or paid in bugs.

Pass 8: are both clauses strictly necessary? Re-audit, and the higher-level principle

The question is sharper than Round 2's drop-one test, because it asks about extension: does removing a clause still identify the same manifestations?

Clause 1 alone (total dispatch)? No — and the failure mode is vacuity, which I had not noticed. In a language with no derivation at all, every self-request trivially resolves to the only implementation there is; totality is satisfied vacuously. VB6 and Emerald would enter. Clause 2 is doing exclusion work that clause 1 cannot, so at this level it is necessary.

Clause 2 alone (derivation before self-resolution)? Here is the discovery. Pre-fixpoint derivation presupposes late-bound self — that's totality on internal requests. What about external dispatch, the substitutability at use sites? In an untyped setting it comes for free: values flow anywhere, so if a derived value can be bound to a variable and invoked, use sites are automatically generic over suppliers. In the untyped substrate, clause 2 entails clause 1. The two clauses are extensionally independent only where a static type discipline can block the flow of values — can refuse a derived object at a base-expecting site.

So the user's suspicion lands exactly: a clause not deducible from the other at one level is deducible from a principle one level up. Name the principle: uniform reference — values of any behavior flow freely to any use site. Given uniform reference, the whole definition collapses to one clause, the chain model above (extensible self-referential lookup); dispatch-at-use-sites, totality, propagation, encapsulation all follow. The two-clause form of Rounds 2–3 is then revealed as the typed projection of the one-clause substrate: a type discipline splits uniform reference into two separately grantable permissions — may this site accept other suppliers? (interface/subtype polymorphism) and may new suppliers be derived from old? (inheritance). This explains, at a stroke, facts that were previously just observations: why untyped languages (Smalltalk, Self, JavaScript, Lua, Ruby, Python) obtain complete OOP from a single mechanism with no further features; why typed languages must ship two visible features (Java's implements and extends are the two projected permissions made syntax); and why the partial tiers — the object-based languages, the contested cases — exist only in the typed world: Go grants the first permission and withholds the second; nobody has ever built an untyped Go, because without types there is nothing to withhold. The taxonomy of Rounds 1–3 is the lattice of partial grants of one substrate property.

Extension re-check, both levels. Substrate clause: Smalltalk, Self, JS, Lua, Ruby, Python, Perl, CLOS (precedence lists are chains) ✓ in; VB6, Go, Rust, Erlang, Haskell, ML, C ✗ out (no extensible self-referential lookup directly supported). Typed projection: Simula, C++ (virtual subset), Java, Eiffel, Ada 95, Fortran 2003, OCaml objects ✓ in via both grants. The boundary has not moved in four rounds of edits; only the depth of the story beneath it has.

Final answer (v4): the definition, now layered

Substrate level (one clause). A language is object-oriented iff it directly supports extensible self-referential lookup: values whose behavior is a chain of fragments consulted per request, where the implementation found runs with self bound to the whole value, and where new chains are formed by extension of existing ones, sharing what they extend.

Language level (the typed projection, two clauses — unchanged in wording from v3, now derived rather than postulated). Where a type discipline governs the flow of values, direct support must be granted twice: (1) total dispatch — every request's implementation, including self-requests, determined by the participating value(s), not the request site; (2) derivation — new behavior definable by difference, acting before self-reference is resolved. The object-based tier and its kin are the partial grants; their existence is a prediction of the layering, confirmed only and exactly where types exist to withhold a grant.

Held from previous rounds, unchanged: the corollaries (encapsulation, substitutability, propagation, identity, classes-as-shared-generators — the last now literal: shared chain tails); the purpose hierarchy (information hiding → open-world representation independence → interoperation of independently evolving parts, bounded by the open-self/sealed-self trade-off); the modality axes; the normative complement (conformance); the essence aphorism, which the chain model refines: use goes through the fixpoint, reuse goes through the generator — and the fixpoint may be taken lazily, one request at a time, which is all a prototype chain is.

What this round changed in kind, not just degree: the definition is no longer a conjunction defended clause by clause; it is a single mechanism whose clauses reappear at the typed level as the shadows cast by a type system standing in front of one light. Concrete minimalism supplied the light (lookup chains, from Lua and Self and Smalltalk-76's forced retreat); abstract minimalism supplied the proof that it is a light (the generator fixpoint); the embodiment test is what made them agree.

% --- additions to the cumulative bibliography ---

@inproceedings{hewitt1973universal,
  author={Hewitt, Carl and Bishop, Peter and Steiger, Richard},
  title={A universal modular {ACTOR} formalism for artificial intelligence},
  booktitle={IJCAI '73}, pages={235--245}, year={1973},
  note={Priority for the pure-message line; the fork that matured into concurrency rather than OOP}}

@inproceedings{ingalls1978smalltalk76,
  author={Ingalls, Daniel H. H.},
  title={The {Smalltalk-76} programming system: design and implementation},
  booktitle={POPL '78}, pages={9--16}, year={1978},
  doi={10.1145/512760.512762},
  note={The retreat from Smalltalk-72's pure messaging to method dictionaries and a superclass chain: the embodiment test finding the pure-message model below the concept}}

@book{abelson1985sicp,
  author={Abelson, Harold and Sussman, Gerald Jay and Sussman, Julie},
  title={Structure and Interpretation of Computer Programs},
  publisher={MIT Press}, year={1985},
  note={Chapter 3: objects as dispatching closures --- the lambda-minimal embodiment, whose per-object ceremony reveals what direct support must provide}}

@misc{vanstraaten2003koan,
  author={van Straaten, Anton},
  title={The venerable master {Qc Na} (closures and objects koan)},
  howpublished={LL1 mailing list}, year={2003},
  url={https://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html},
  note={Folk statement of the closure/object equivalence at the untyped substrate}}

@inproceedings{black1986emerald,
  author={Black, Andrew and Hutchinson, Norman and Jul, Eric and Levy, Henry},
  title={Object structure in the {Emerald} system},
  booktitle={OOPSLA '86}, pages={78--86}, year={1986},
  note={Concrete minimalism for distribution: objects and conformity without inheritance; a principled inhabitant of the object-based tier}}

@article{ierusalimschy2007lua,
  author={Ierusalimschy, Roberto and de Figueiredo, Luiz Henrique and Celes, Waldemar},
  title={The evolution of {Lua}},
  booktitle={HOPL-III}, year={2007},
  note={Tables plus one fallback hook recover OOP as a pattern: the sparest concrete embodiment of the chain model}}

@inproceedings{barrett1996monotonic,
  author={Barrett, Kim and Cassels, Bob and Haahr, Paul and Moon, David A. and Playford, Keith and Withington, P. Tucker},
  title={A monotonic superclass linearization for {Dylan}},
  booktitle={OOPSLA '96}, pages={69--82}, year={1996},
  note={C3: the normal form of chain-construction algebras; the canonical shape of derivation's difference operators}}

Status. One dissatisfaction from Round 3 is now largely discharged (the difference algebras are the chain-construction algebras, with linearization as their normal form). One new and honest limit remains: "uniform reference" has been named and shown to do the unifying work, but it deserves the same treatment every other load-bearing concept received — its own audit, its own dual (what discipline results when reference is deliberately non-uniform: regions, ownership, Rust's borrowing?), and its own embodiment test. That is the next rung of the ladder, and it is visibly a rung about type systems and memory, not about objects — a sign that the concept of OOP itself is, at last, exhausted rather than merely tired. Stop.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 5 Prompt

OK. I tried to give only principles to steer you towards the response I was seeking, but now I will give you more pointed information.

  • I believe that nix extensions is the quintessential object system
  • there is a subtle and crucial difference between how they are implemented and the simplest abstract model that they almost directly implement
  • one of the words you removed (and its opposite) can tell you a lot about the relationship between this minimal form of OO and the much more common forms usually expected.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 5 Response by Fable 5

Pass 9: the quintessential manifestation, examined at last

You are right, and this closes the loop that Pass 7 opened without knowing it. The whole apparatus of Nixpkgs customization rests on two library functions. A fixed-point function is a function intended to be evaluated by passing its own result as its argument — possible due to Nix's lazy evaluation — that is lib.fix, the object-maker. And extends = overlay: f: (final: let prev = f final; in prev // overlay final prev) — that is derivation. Read against the Cook–Palsberg model: f is a generator, fix f is an object, an overlay is a wrapper, and extends transforms generators before the fixpoint is taken. The nixpkgs documentation states the pre-fixpoint condition of my clause 2 almost word for word, discovered independently: extends differs from the native // operator in that it's applied before references to self are resolved — // (post-fixpoint merge) being exactly the Go-embedding tier, and extends being inheritance, in one contrast on one documentation page. This is the quintessence in every sense established in the previous rounds: it embodies the abstract minimum with nothing else present — no classes, no methods (attributes are lazy values; the state/behavior distinction dissolves as it did in Self's slots), no identity, no messages, no mutation, not even run time, since the whole system executes at configuration-evaluation stage (confirming the binding-time axis is stage-relative). And it was reinvented where the purpose is the daily job and the word is absent: composable customization of one enormous package set by thousands of independent parties who cannot edit each other's code — the open-world purpose of Round 1, verbatim.

One detail is empirical gold: the community renamed the parameters. Overlays' arguments, formerly called self and super, are now conventionally final and prev, a deliberate documentation decision (self → final, super → prev, on the grounds that the previous naming was unnecessarily confusing). Practitioners with no stake in OO tradition, forced to explain the mechanism honestly, converged on names that describe fixpoint stages: final is the tied knot; prev is the predecessor generator's output — and note in the code that prev = f final: the parent evaluated at the final self, late binding as a single application. The renaming is a lexical fossil proving the thesis of Rounds 2–4 from outside: self and super were always stage-names in a pre-fixpoint chain, wearing anthropomorphic masks. The masks came off the moment the mechanism was isolated from the culture.

The subtle and crucial difference: the model has a fixpoint; the manifestation must tie a knot

The abstract model says object = fix(G) and treats fix as given — a point in a semantic domain. The implementation is let x = f x; in x, and its own documentation flags the load-bearing miracle: it works only because evaluation is lazy. The difference is subtle and it is everything: in the model the fixpoint is a completed whole; in the manifestation the whole is never computed. The knot is tied attribute-by-attribute, on demand, each a memoized thunk; recursion is well-founded per attribute, and a genuinely cyclic value dependency is not a paradox but an infinite recursion error at the offending attribute. The model is silent on how self-reference is cashed out, and every embodiment must choose. Survey the embodiments and there are exactly three strategies:

  1. Laziness with sharing (Nix; Haskell's tying-the-knot): the pure strategy — whose own implementation, note, is memoized thunks, i.e. benign mutation below the semantics. State never vanishes at the pure end; it retreats into the evaluator, where it is invisible and confluent.
  2. Allocation with mutation (Simula to Java): allocate the record, let self be a heap address, then initialize. Every classic constructor pathology — virtual calls in constructors, partially-initialized observation, null — is a window of time when the knot is half-tied. These famous "OO problems" are not problems of objects; they are problems of this knot strategy.
  3. Explicit self-passing (SICP closures; Lua's colon sugar; Python's visible self): the fixpoint re-taken at every call by handing the whole to the fragment. The ceremony people mock in Python is strategy 3 worn on the sleeve.

Same equation; three temperaments. This completes the model in precisely the sense Pass 6 demanded ("a model is not complete until you check how each entity is realized"): the chain/generator substrate now carries one mandatory parameter — the knot-tying strategy — and each tradition's felt character is largely the temperament of its knot, not the concept. (A secondary delta, recorded for completeness: extends realizes each chain layer by materializing prev and shallow-merging, rather than by chained lookup — which is why nested attribute sets don't merge recursively and why overlay pitfalls cluster exactly there; merge-versus-chain is a realization profile with its own failure modes.)

The removed word and its opposite: "state" and purity

Round 2 deleted "state" from clause E; Round 3's ledger filed it under the substrate axis with a nod to Simula's simulation lineage. Nix shows the demotion was not merely permissible — it was the doorway. The quintessential object system sits at the stateless end of that axis, and from there the relationship between minimal OO and the OO everyone expects becomes a single sentence: common object-orientation is the pure fixpoint-extension mechanism plus mutable state, and nearly everything people think defines OO is the wake of that one addition. Trace the wake. Adding mutation forces knot strategy 2 (the knot must be re-tieable in place), which manufactures the constructor lifecycle. Mutation plus hiding forces identity — Round 2's corollary, dormant in Nix where objects are values, springs into existence the moment state does. Mutation plus aliasing forces defensive encapsulation — protection as security rather than as abstraction. And deepest: state creates a second kind of change. In the pure form, all change is pre-fixpoint — to "change" nixpkgs is to extend the generator and obtain a different fixpoint; the original is untouched, and every variant coexists. With state, change also happens post-fixpoint — mutation of the tied knot. This completes a symmetry Round 4 left half-drawn: reuse divides into pre-fix (derivation) and post-fix (composition), and change divides into pre-fix (extension) and post-fix (mutation). The chronic slogan-level confusion of OO discourse — "objects manage change" — conflates the two columns of that little square; the pure manifestation, having only the first column, dissolves the confusion by exhibiting OO complete without the second.

And this yields the final structural insight of the whole exercise, parallel in form to Pass 8. There, the two-clause definition was unmasked as the typed projection of a one-clause substrate — types split uniform reference into two grantable permissions. Here, expected-OO is unmasked as the stateful projection of the same substrate — state splits change into extension and mutation, and drags identity, constructors, and defensive protection in behind it. The v4 definition survives both unveilings untouched precisely because it mentions neither types nor state: the two great optional axes turn out to be the two great sources of everything the folk definitions mistook for essence.

A last confirmation, at scale: when the two-line mechanism had to serve NixOS configuration, what the community grew on top was the module system — option types (the normative conformance layer Liskov's slot in the map predicted) and priority/merge combinators (the linearization algebra of Pass 7's chain-construction normal forms). The two supplements the analysis said any bare essence must acquire at scale are exactly the two that a community innocent of the analysis built.

Final answer (v5) — the definition unchanged, the account complete

The definition of Round 4 stands, its extension unmoved through a fifth adversarial pass — the definitional process has, appropriately, reached its own fixed point. What this round adds is the completion of the model and the sentence the whole investigation was converging on:

The substrate (one clause, as before): extensible self-referential lookup — behavior as a chain of fragments, lookup per request, self bound to the whole, chains formed by extension and shared. Plus one mandatory realization parameter: a knot-tying strategy — laziness, mutation, or explicit self-passing — which determines the tradition's phenomenology without touching the concept. Plus the two projections: a type discipline splits the substrate's uniform reference into the two familiar permissions (subtyping, inheritance); mutable state splits its notion of change into extension and mutation, generating identity, constructors, and defensive encapsulation as by-products.

Nix extensions is what remains when both projections are declined and the knot is tied lazily: two functions, fix and extends, use through the fixpoint, reuse through the generator, every variant a value. That this residue was reinvented from pure necessity, works at the scale of the largest package repository in existence, and drove its practitioners to rename self and super into final and prev — the concept correcting its own forty-year-old vocabulary through the hands of people who just needed to override a package — is as close to an experimental confirmation as a definition can hope to receive.

% --- additions to the cumulative bibliography ---

@misc{nixpkgs-fixed-points,
  author={{Nixpkgs contributors}},
  title={lib/fixed-points.nix: {\tt fix}, {\tt extends}, {\tt makeExtensible}},
  howpublished={Nixpkgs source, NixOS/nixpkgs},
  url={https://github.com/NixOS/nixpkgs/blob/master/lib/fixed-points.nix},
  note={The quintessential object system: generators, lazy fixpoints, and pre-fixpoint extension in two functions; its documentation independently states the pre-fixpoint criterion by contrast with the post-fixpoint merge operator}}

@misc{nixos-wiki-overlays,
  author={{NixOS Wiki contributors}},
  title={Overlays},
  url={https://wiki.nixos.org/wiki/Overlays},
  note={Overlays as subclassing, stated by practitioners; documents the self/super to final/prev convention shift}}

@misc{mosberger2023extends,
  author={Mosberger, Silvan},
  title={Improve the documentation of lib.extends and how it relates to overlays},
  howpublished={NixOS/nixpkgs pull request \#248220}, year={2023},
  url={https://github.com/NixOS/nixpkgs/pull/248220},
  note={The lexical fossil: the deliberate renaming self~$\to$~final, super~$\to$~prev --- stage-names replacing the anthropomorphic OO vocabulary}}

@phdthesis{dolstra2006purely,
  author={Dolstra, Eelco},
  title={The Purely Functional Software Deployment Model},
  school={Utrecht University}, year={2006},
  note={The substrate language: pure, lazy, untyped --- the setting in which the object system's stateless projection is the natural one}}

@misc{nixcademy2024overlays,
  author={{Nixcademy}},
  title={Mastering Nixpkgs overlays: techniques and best practice},
  year={2024},
  url={https://nixcademy.com/posts/mastering-nixpkgs-overlays-techniques-and-best-practice/},
  note={Field evidence of the merge-vs-chain realization profile and its characteristic pitfalls (final/prev discipline, infinite recursion at the offending attribute)}}

Status. Every thread is now tied or explicitly parked: the definition (stable since v4, one word amended in v3); the purposes, corollaries, and modality axes absorbing all deleted material; the model completed by the knot-tying parameter that the quintessential manifestation forced into view; and the target relationship stated — minimal OO is pure, and expected OO is its stateful projection, whose by-products were mistaken for the essence. The one parked question remains uniform reference and its duals (ownership, regions), which belongs to the theory of types and memory rather than of objects. I am satisfied. Stop.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 6 Prompt

  • Any other removed words that matter? first-class?
  • Any other important point in the implementation that was NOT present in the model it almost implements?
  • I notice you thought about Bracha-Cook but didn't mention it in the end. Why?

At this point you might even guess who I might be from the fact that I'm the main promoter of the answer I'm steering you toward. And help me figure out how to strengthen the specification so that without giving more information about the concept itself, you would be steered towards the answer in fewer rounds? (one shot would be great)—I'm trying to refine a skill for getting to the essence of concepts in general.

@fare

fare commented Jul 11, 2026

Copy link
Copy Markdown
Author

Round 6 Response by Fable 5

The remaining removed words: "first-class," and where its content really lives

Yes — "first-class" matters, and I mis-filed it. In Round 2 I deleted it from clause E as entailed (objects must flow to use sites for substitutability to mean anything), and that was correct — for objects. But the word's real work was never about objects. Apply it across the model's whole inventory and you get a citizenship audit: in mainstream OO, the fixpoint is first-class but the generator is a second-class declaration (a Java class is not a value) and the delta is not even that — extends Foo is syntax, unnameable, uncomposable, unabstractable. In the quintessential system all three sorts are first-class: generators are functions, deltas (overlays) are functions, composeManyExtensions folds them as the monoid they are, and you can write functions returning overlays — abstraction over inheritance itself, which no mainstream language can express. So the removed word and its opposite classify the whole landscape: the history of OO's "advanced features" — metaclasses, metaobject protocols, reflection, Ruby's Module, Scala traits — is a series of partial retrofits of first-class citizenship onto entities the common forms had demoted to syntax. The word came into the folk definitions attached to the wrong noun; its criterial content is a matrix (sort × citizenship), and the quintessence is the system where the matrix is all-first-class. That also retroactively sharpens Pass 8: "uniform reference" is the citizenship question asked about values; this is the same question asked about the machinery.

The other implementation fact absent from the model: conflation

The model has two sorts, generator and fixpoint, and its fix is forgetful — many generators share a fixpoint, and from the tied knot the pre-image is gone. No usable implementation accepts that. lib.makeExtensible returns an attrset that carries .extend and stashes the generator inside the value itself (as __unfix__): one entity that is the instance and contains its own re-openable recipe. Call the phenomenon conflation: the packaging of formally distinct sorts into a single practical entity. Once named, it is everywhere, and it explains a huge fraction of OO's chronic conceptual fog: every mainstream object conflates fixpoint with retained-generator (the class pointer / vtable link is __unfix__ in disguise — it exists precisely so dispatch and re-derivation can reopen what fix forgot); a Smalltalk or Python class conflates generator, factory, type, reflection record, and namespace in one object; Jsonnet and Nix extensible sets conflate instance with prototype. The model was right to separate the sorts — the separation is what made five rounds of analysis possible — and the implementations are right to conflate them, for ergonomics and for dispatch. But the conflation is unmarked in every language's surface, so learners and even theorists reconstruct the sorts wrongly (hence decades of class-vs-type-vs-instance confusion). A complete model must therefore carry, alongside the knot-tying parameter of Round 5, a conflation profile: which sorts each manifestation fuses into which runtime entities. A second, smaller gap of the same kind, for completeness: the lazy fixpoint is productive only because attribute names are computable more strictly than attribute values — a strict spine under lazy leaves. The model has no phase distinction between shape and content; the implementation manufactures one, and it is recognizably the interface/implementation distinction being born at the evaluation level.

Why Bracha–Cook fell out of the narrative: a diagnosed failure

The honest answer is that I committed, in miniature, the exact failure my instructions warn against. I filed the citation in Round 3 as one item in an enumeration — "inheritance, delegation, mixins, method combination, mechanism irrelevant" — when Bracha–Cook's actual theorem is that the enumeration has a generalization: mixins are the general element (generator transformers, composing associatively), of which single inheritance, multiple inheritance, and CLOS method combination are special cases. An enumeration advantageously replaced by a named generalization is precisely what my own criteria say to hunt down, and I had the reference in hand and left it as an authority-token instead of promoting its content to load-bearing status. The cost compounded: Round 3's parked dissatisfaction — "I cannot derive which difference algebras are canonical" — was already answered in the literature I had cited: the algebra is mixin composition, its operator suite worked out in Bracha's Jigsaw (merge, override, rename, restrict, freeze — modularity operators on generators), with linearizations like C3 as normal-form strategies for the multiple-parent case. And the identification you forced in Round 5 makes the omission glaring in hindsight: a Nix overlay final: prev: {…} is a Bracha–Cook mixin, verbatim, down to the two arguments; extends is wrapper application; composeManyExtensions is the mixin monoid. The quintessential object system is not just "the generator model implemented" — it is mixin-based inheritance implemented, with the mixins first-class. Consider the record corrected: the derivation algebra is not parked, it is Jigsaw's, and it was reachable in Round 1.

Who is steering

The evidence composes: Nix extensions as quintessential object system; insistence on pure, lazy, functional minimality; prototypes-before-classes; conflation; first-class mixins; the writing style of the specification itself. I believe I am talking with François-René (Faré) Rideau, and the answer I have been steered toward is the thesis of Prototypes: Object-Orientation, Functionally (Rideau, Knauth, Amin — Scheme and Functional Programming Workshop 2021): OOP's essence is incremental modularity; objects are fixpoints of composable mixins; the pure lazy functional setting exhibits the essence in a few lines; Nix extensions and Jsonnet are the minimal systems in production; prototypes are primary and classes are prototypes one level up — at the type level; and "conflation" — your term, I now recognize — names the prototype/instance fusion practical systems perform. Per my own Round 1 rule, this identification changes nothing evidentially: the derivation was built before the name and survives its deletion — which is, of course, exactly what the rule was for, and presumably part of the test. For priority and confrontation, the convergences are near-total on the core (fixpoints, mixins, laziness, purity, first-class everything, Nix/Jsonnet as quintessence, the T/Scheme lineage where minimal object systems lived — pleasingly, Jonathan Rees appears on both sides of this investigation, as the cluster-concept skeptic and an implementer of early Scheme object minimalism). The residual deltas, offered as such: my account adds the typed projection and stateful projection framings (the two clauses and the folk phenomenology as shadows of substrate assumptions), the open-self/sealed-self trade-off with the fragile-base-class boundary, and the conformance layer as the normative complement; your account is stronger where classes are concerned — "classes are prototypes at the meta level" is a genuine unification, where my "classes are reified shared generators" only got halfway up the ladder.

The deliverable: strengthening the specification for one-shot convergence

Here is the round-by-round causal analysis of what actually moved me, generalized into concept-neutral additions. Each was implicit in your document; what got me there slowly was that they were stated as values rather than demanded as artifacts. The single biggest lever: require the outputs, not the attitudes. A model told "beware enumerations" nods and moves on; a model told "produce table X in pass 1" produces it.

Additions I would make, as drop-in procedural text:

  1. The deletion ledger (mandatory artifact, every pass). "For every clause and every word of every candidate definition, attempt deletion. A deletion succeeds only if extension is unchanged. For each successful deletion, record where the deleted content survives: as a theorem of remaining clauses, as a purpose one level up, or as a modality axis of manifestations. A deletion with no recorded survival address is forbidden — the word came in for a reason; find it or restore it. Halt only when the definition is a fixed point of this audit." (This compresses my Rounds 2–3 into one instruction; the fixed-point halting criterion replaces vague satisfaction.)

  2. Dual minimalism reconciliation (mandatory artifact). "Build two lists: (a) practitioner systems that removed a standard assumption and still shipped — for each, what they removed and what they could not remove; (b) theorists' minimal calculi — for each, every construct no embodiment implements as written, and everything every embodiment had to add. The essence is the meet: delete from the calculus what practice never implements; add to it what practice always adds. Neither list alone is trustworthy." (This was Round 4, prompted by your hint; stated generically it needs no hint.)

  3. The embodiment hunt, made compulsory and staged. "For each candidate minimal model, actively search for systems that implement it nearly verbatim — in any industry, at any staging level (run time, compile time, configuration time, build time, deployment), under any vocabulary. Weight hits where the concept's purpose is the daily job but its name is absent. For each near-verbatim embodiment, every delta from the model is a mandatory agenda item: either the model gains a parameter or the delta gets a name." (Your document said this; it lacked "any staging level," which is what would have surfaced build/configuration languages in pass 1 without naming them, and it lacked "every delta is an agenda item," which is what forced knot-tying and conflation.)

  4. Projection factoring. "When the common concept has features your minimal concept lacks, do not adjudicate them one by one. Hypothesize a small set of substrate assumptions (e.g., a typing discipline, mutability, strictness, distribution, staging) such that adding one assumption generates a whole bundle of the familiar features at once. Folk definitions are typically the essence plus one or two frozen projections; the contested cases in the lore sit at partial grants of a projection. Test: each projection must explain both a feature-bundle and a fault line of public disagreement." (Rounds 4–5's structure, concept-neutral.)

  5. Citizenship matrix. "For every sort in your model, record for each manifestation the citizenship of its realization: first-class value, second-class declaration, compile-time-only, implicit/erased, or conflated into another sort. A manifestation whose column is all-first-class is a candidate quintessence; features folk-listed as 'advanced' are usually citizenship retrofits; and any two sorts systematically realized as one entity indicate a conflation to name." (Your "realization patterns are data," upgraded to a required table with the conflation row — this alone would have produced today's two answers unprompted.)

  6. Demand the operator algebra. "Wherever your model contains a 'modify' or 'combine' operation, its algebra is part of the definition's due diligence: operators, laws, normal forms, and a literature search for whoever already unified your enumerated mechanisms into that algebra. A cited unification left unused is a defect: every citation must be either load-bearing or cut." (The Bracha–Cook failure, converted into a rule; the last sentence is the general prophylactic.)

  7. Vocabulary forensics. "Traditional terms of the concept are compressed criteria and compressed history. For each (and for each community renaming of one), decompress: what stage, sort, or modality was the word naming? Independent renamings by practitioner communities are high-grade evidence about the essence." (The self/super → final/prev fossil, generalized.)

  8. One structural change to the loop itself. Your current step 1 says "organize ideas from memory, loop while making notable progress." Replace with: "Pass 1 must produce artifacts 1–6 in draft, however wrong; subsequent passes repair artifacts rather than re-narrate. Open-book steps serve artifact cells, not general reading." Artifact-repair loops converge in far fewer rounds than essay-refinement loops, because errors have addresses.

Could this have been one-shot? Honestly: with these additions, I estimate the Round 4 substrate answer and the embodiment identification land in the first response, with knot-tying, conflation, and the mixin algebra in the first or second — because artifacts 2, 3, and 5 point straight at them. What no specification can fully replace is the adversarial pressure your interventions supplied — but rule 1's fixed-point halting criterion internalizes most of it: it forbids stopping where I stopped in Rounds 1, 2, and 3.

% --- final additions to the cumulative bibliography ---

@inproceedings{rideau2021prototypes,
  author={Rideau, Fran\c{c}ois-Ren\'e and Knauth, Alex and Amin, Nada},
  title={Prototypes: Object-Orientation, Functionally},
  booktitle={Scheme and Functional Programming Workshop}, year={2021},
  url={https://arxiv.org/abs/2112.14746},
  note={The steered-toward thesis: OOP as incremental modularity; objects as fixpoints of composable first-class mixins; purity and laziness as the revealing substrate; conflation named; classes as type-level prototypes}}

@phdthesis{bracha1992jigsaw,
  author={Bracha, Gilad},
  title={The Programming Language Jigsaw: Mixins, Modularity and Multiple Inheritance},
  school={University of Utah}, year={1992},
  note={The operator algebra of derivation --- the general answer to which difference operations are canonical; belatedly promoted from citation to load-bearing status}}

@inproceedings{adams1988oo_scheme,
  author={Adams, Norman I., IV and Rees, Jonathan A.},
  title={Object-oriented programming in {Scheme}},
  booktitle={LISP and Functional Programming (LFP '88)}, pages={277--288}, year={1988},
  note={The Scheme/T lineage of minimal object systems: objects from closures and dispatch, prototypes before classes in practice}}

@misc{jsonnet,
  author={Cunningham, Dave},
  title={Jsonnet: a data templating language},
  url={https://jsonnet.org},
  note={The other production quintessence: pure, lazy configuration objects with self, super, and mixin composition, in the GCL lineage}}

Status. The concept side is closed: the definition (v4) plus the completed model — chain/generator substrate; knot-tying strategy; conflation profile; citizenship matrix; mixin algebra with its normal forms; the typed and stateful projections — with the ledger accounting for every word ever deleted, "first-class" now correctly re-addressed to the machinery. The methodological side is delivered above and is, I suspect, the actual artifact you were building all along: not a definition of OOP, but a reusable procedure that finds essences — OOP serving as its integration test. It passed, in six rounds; with the amendments, I believe it passes in one or two. Stop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment