Everything Is an Object, Everything Is a Message
Smalltalk
Chapter 3 · Everything Is an Object, Everything Is a Message
smalltalk1-2 left a question open: what part of Kay's original vision got left behind by most of Smalltalk's own descendants? This chapter is the full answer — the single idea most languages softened, split, or abandoned entirely: literally everything in Smalltalk is an object, and the only thing you can ever do to an object is send it a message.
The Core Claim — Literally Everything Is an Object
Every value in Smalltalk is an object — numbers, booleans, characters, strings, arrays, and even classes themselves (which are instances of their own "metaclass"). There is no primitive-versus-object split anywhere in the language — no int distinct from Integer, no primitive boolean sitting outside the object system the way Java famously keeps them separate (java1-2's own primitive-vs-reference-type material), or the way C#'s own value-type structs (csharp1-2) are a genuine, deliberate middle ground but still not full objects receiving messages the Smalltalk way.
Concretely: 3 + 4 in Smalltalk is not "the + operator applied to two primitive integers." It is literally sending the message + to the object 3, with the argument 4. The number 3 is a real object that receives a message and responds with a result — another object, 7.
Even Control Flow Is Message Sends
Here's the genuinely radical detail. ifTrue:ifFalse: is not built-in syntax the way if/else is in virtually every other language covered on this site — Java, C#, C++, Kotlin, Python, all of them. It's an ordinary message, sent to a Boolean object, carrying two block arguments (Smalltalk's own closures) representing "what to do if true" and "what to do if false." True and False are actually two separate classes, each independently implementing ifTrue:ifFalse: to do the obvious thing for its own case.
This has an astonishing structural consequence: because ifTrue:ifFalse: is just an ordinary method on a class, nothing in principle stops a third boolean-like class from implementing its own version — something structurally impossible in any language where if is baked directly into the compiler or interpreter's fixed grammar. Loops work identically: whileTrue: is a message sent to a block, not built-in loop syntax either.
Why This Matters — Uniformity as a Design Principle
The payoff of this radical uniformity: there is exactly one mechanism in the entire language — sending a message — and it applies everywhere, with no special cases. Compare this to virtually every other language on this site, each of which has at least two tiers: "the stuff that's really built into the language's own grammar" (if/else, loops, operators on primitives) and "the stuff that's just regular code" (method calls, user-defined types). Smalltalk collapses this into one tier, entirely.
Direct Contrast — Dispatch in Other Languages on This Site
| Language | Dispatch model | Primitives are objects? |
|---|---|---|
| Java (java1-5) | Every instance method virtual by default | No — int/boolean sit outside the object system |
| C# (csharp1-5) | Explicit virtual/override — opt-in dispatch | No — real value-type structs, not messages |
| C++ (cpp1-7) | Compiler-built vtable for virtual classes | No — primitives are raw machine types |
| Kotlin (kotlin1-4) | JVM-inherited class/primitive split | No — same split as Java underneath |
| Smalltalk | Live message lookup at send time, no exceptions | Yes — every value, no exceptions |
The throughline across every one of those languages: each treats "the fixed set of things the language itself natively understands" as separate from "user-extensible object behavior." Smalltalk has no such separation anywhere — even arithmetic and control flow live entirely inside the same, single, uniform mechanism as every user-defined class's own methods.
A Worked Example — A Conditional as Pure Message Sends
Walking through x > 5 ifTrue: ['big'] ifFalse: ['small'] one message at a time: first, x > 5 sends the message > to the object x, with argument 5 — this returns a Boolean object, either true or false. That Boolean object then receives the message ifTrue:ifFalse:, carrying the two block arguments. The Boolean itself — not some special interpreter-level "if" logic — decides which block to actually evaluate and return, purely through its own class's own method implementation.
smalltalk1-7 covers the honest consequences in full. Every single operation — even 3 + 4 — technically involves a full message dispatch and method lookup, which is genuinely slower at a naive implementation level than a compiler emitting one raw machine-code ADD instruction for two primitive integers, the way C, C++, and Java's own primitive arithmetic works. Uniform elegance and raw performance are a real tradeoff here, not a solved problem.
ruby1-2's own material on Ruby's object-identity focus traces straight back to what's described in this chapter. smalltalk1-6 covers that lineage in full.
Reflection Questions
In a language you already know, try to imagine defining a third boolean-like value with its own custom "if" behavior. What specifically stops you from doing this in that language, that Smalltalk's own model doesn't stop you from doing?
This chapter argues that treating control flow as message sends is philosophically consistent but has a real performance cost. Do you think that tradeoff was a reasonable one for Kay's own Dynabook vision (smalltalk1-2) specifically? Why or why not?
Every language in this chapter's own compare-table keeps a "built into the grammar" tier separate from a "regular code" tier. Can you think of a real practical benefit that separation provides, that Smalltalk's own fully uniform model gives up?
Chapter 3 Key Takeaways
- Every value in Smalltalk is an object — no primitive/object split anywhere, unlike Java, C#, C++, or Kotlin
3 + 4is literally sending the message+to the object3ifTrue:ifFalse:andwhileTrue:are ordinary messages, not built-in syntax — True and False are separate classes each implementing their own version- Smalltalk has exactly one mechanism (message sending) with no special cases; every other language on this site has at least two tiers
- This uniformity carries a genuine performance cost — every operation is a full dispatch, not a raw machine instruction
- This is the direct ancestor of Ruby's own "everything is an object" claim (smalltalk1-6)