C:\ANCIENT\SMALLTAL.TXT
1972
Smalltalk-72: Ask the Receiver What a Message Means
Early Smalltalk organized computation around objects and messages; a checked clock-and-walker model explains shared vocabulary, independent state and receiver-defined behavior.
Ask a clock to advance twenty and ask a walker to advance twenty. The wording is the same. The expected results aren’t.
The clock changes a time. The walker changes a position. Each request becomes useful because something receives it and knows how to interpret it.
Early Smalltalk made objects and their messages central to organizing computation. That is an idea worth separating from the later syntax most programmers associate with the name.
Our example will use two invented receivers with independent state. It shows what that arrangement buys, which agreements the caller needs, and what sending a message doesn’t automatically guarantee.
The early system and the later familiar one
Alan Kay’s 1993 history recalls Dan Ingalls implementing the first running interpreter on a Nova in BASIC, followed by Kay’s November 1972 demonstration of the interpretation scheme. Kay also emphasizes Ingalls, Adele Goldberg and the broader PARC group’s contributions.1
Smalltalk-72 let a receiver interpret the remainder of a program list as its message. Smalltalk-76 and -80 developed different internal and language arrangements. Their familiar source and virtual-machine conventions shouldn’t be inserted into the 1972 beginning.1
The code behind our companion check is modern Python. The reader-facing request descriptions are original pseudocode, not purported Smalltalk-72 statements. A shared idea can be explained without pretending a later implementation is the original one.
One word, two responsibilities
Choose a message called advance with one nonnegative integer argument. Send it first to a Clock receiver whose stored value is the minute within a chosen twenty-four-hour day.
Clock A begins at minute 1430, corresponding to 23:50. Advance twenty means add twenty minutes and wrap at 1440. The resulting stored minute is ten, and the reply is 00:10.
Now send the same request to a Walker receiver. It begins at position (2, 3), with heading (1, 0): positive x. Advance twenty means move twenty units along that heading. The resulting position and reply are (22, 3).
The two receivers interpret the argument in different units. Minutes belong to the Clock contract; distance units belong to the Walker contract.
Sharing a message name doesn’t imply that its number has one universal meaning. A caller has to understand the kind of receiver it selected.
Original explanatory SVG, created in 2026. Chosen receivers, rules and values; not Smalltalk-72 source syntax or a recovered interpreter trace.
The clock didn’t need to know how the walker moves. The walker didn’t need to understand midnight. Each handled its request through its own rule.
Choose the receiver before changing state
Our model dispatcher accepts a receiver, a message name and an argument. It finds the receiver’s handler and allows that handler to update the receiver’s state.
That order is useful. The caller doesn’t open a general-purpose memory record and decide which field to alter for every possible object.
For Clock, the handler validates the argument, calculates the wrapped minute and stores it. For Walker, it validates the argument, uses the stored heading to calculate a new position, then stores that result.
A deliberate failure test sends an undefined message called teleport to Clock. Our chosen model reports an error and leaves the minute unchanged.
This is a teaching policy. It isn’t a claim about Smalltalk-72’s exact error machinery. It gives the example a clear boundary: an unknown request doesn’t secretly become a different known operation.
The checker also rejects a negative amount under our chosen advance contract. A different design could allow backward movement, but it would need to state that agreement. The reassuring word advance doesn’t settle the validation rule by itself.
Shared behavior, separate instances
Create Clock B with stored minute one hundred, corresponding to 01:40. Clock A and Clock B use the same rule for advance, but they don’t share one mutable minute value.
Send advance twenty to A. A moves from 1430 to ten. B stays at one hundred, because it hasn’t received a request that changes its state.
Send advance five to B afterward. B becomes one hundred five, corresponding to 01:45. A remains ten.
The companion checker tests both directions. It would catch a mistaken implementation that put the minute in one shared mutable record and made every clock change together.
Original explanatory SVG, created in 2026. Chosen wrap and validation rules; no historical security or delivery guarantee implied.
Shared behavior lets us describe the rule once. Separate instance state lets several receivers represent several clocks. Those two forms of sharing should remain distinguishable in the implementation.
Kay’s account makes own memory and message exchange central to its description of objects.1 Our clock model supplies concrete arithmetic for that idea rather than reproducing the historical object representation.
A reply is part of the contract
The Clock reply is readable time text. The Walker reply is a coordinate pair. Both answer advance, but callers can’t blindly apply the same operation to their replies.
Our chosen batch caller collects labeled results: receiver A with 00:10, Walker with (22, 3). It preserves the association between a reply and the receiver that produced it.
If the application only needs to know that each request succeeded, it can ignore those different values. If it needs to display a meaningful report, it must interpret them appropriately.
A common message vocabulary therefore supports a common calling pattern. It doesn’t automatically make every result the same type or every request interchangeable in every context.
The checker verifies both the state change and reply. A handler that calculates the right new minute but returns yesterday’s time still breaks our stated agreement.
Order can change the result
The Walker also accepts a chosen heading request. Start at (2, 3) facing right. Advance twenty to reach (22, 3), turn upward and advance two. The final position is (22, 5).
Reverse the placement of the first turn: face upward before advancing twenty, then advance two while still facing up. The final position becomes (2, 25).
The messages are individually valid in both sequences. Their order changes the stored heading used by movement.
This example runs every handler immediately and finishes it before the next request. There is no asynchronous queue or distributed delivery. A message metaphor doesn’t, by itself, establish those mechanisms.
The checker compares the two sequences, ensuring the difference is intentional. It also verifies that changing heading alone leaves position unchanged. An orientation field shouldn’t move the object merely because its next movement will use a new direction.
Hide a representation behind a stable request
Our first Walker representation stores its current x and y directly. A second original model stores an origin and an accumulated offset. Its reported position is origin plus offset.
Starting both at (2, 3) and applying advance twenty, heading upward and advance two gives (22, 5) in each model. The internal records differ; the external requests and replies agree.
That is one reason to route work through receiver behavior. A caller that depends only on this tested contract can continue to work when the internal storage changes.
A caller that directly reads a private-looking x field would fail with the second representation, which has origin and offset instead. Avoiding that dependency is a design practice in our example, not a proven security barrier against arbitrary Python inspection.
Likewise, Kay’s object-protection ambitions shouldn’t be inflated into a claim that every early implementation enforced a universal isolation boundary.1
Flexibility still needs a grammar
A receiver needs enough information to distinguish requests, arguments and expressions. Smalltalk-72’s receiver-directed interpretation was a different arrangement from later fixed source conventions.1
Our dispatcher deliberately avoids reconstructing that parser. It receives an already separated message name and integer. This makes state and behavior easy to inspect while leaving language interpretation outside the check.
That limitation matters. Correctly adding twenty in Python doesn’t verify how an original Smalltalk program divided its tokens or evaluated an expression in a sender’s context.
It verifies a smaller claim: once our model has identified this receiver, this request and this argument, the receiver performs the rule we defined.
Test the boundary, not just the happy result
The check covers midnight wrap, independent clocks, unknown requests, invalid amounts, heading-only updates, order-sensitive movement and two representations with matching observable behavior.
For an additional boundary, advancing Clock A by a full chosen day leaves its minute unchanged. That result follows from modulo 1440, though it wouldn’t mean that no duration had passed in an application that also stored a date. Our example stores no date, and says so.
The modern model doesn’t execute a Smalltalk-72 interpreter, simulate a Nova or verify a Smalltalk-80 virtual machine. It doesn’t measure historical performance. Its purpose is to make the message contract concrete enough that mistakes have identifiable consequences.
The invalid-request checks compare every stored field before and after failure. That avoids a subtle partial update: reporting an error after changing the minute would leave a caller with both a failure and a changed clock. Our chosen handlers validate first, then perform the update.
A small receiver can own a useful rule
I find the clock-and-walker pair useful because it keeps the metaphor honest. Each receiver owns state and interprets a request through a particular agreement. There is still work to do around units, ordering, replies and failure.
Early Smalltalk explored that arrangement as a basis for a computing system. Our original examples show why the idea is appealing: the caller can ask for behavior while the receiver remains responsible for what that behavior means.
Footnotes
-
Alan C. Kay, “The Early History of Smalltalk”, HOPL-II, 1993. Introduction, first running interpreter, November demonstration, Smalltalk-72 principles and later-version distinctions inspected. All clock/walker requests, validation, replies and representations are original models, not recovered language syntax. ↩ ↩2 ↩3 ↩4 ↩5