Smalltalk — Reference

Source: https://pharo.org/

Smalltalk

  • Created: 1972 (Smalltalk-72), Xerox PARC, by Alan Kay, Dan Ingalls, Adele Goldberg and the Learning Research Group; standardized as Smalltalk-80, ANSI standard 1998 (ANSI INCITS 319-1998). This note focuses on Pharo (the most active modern lineage) with notes on Squeak, GemStone/S, VAST, and Cuis.
  • Latest stable: Pharo 13.1 (June 26, 2025; Pharo 13.0 May 21, 2025). Squeak 6.0 (Jun 2022, latest at time of writing). https://pharo.org/news, https://squeak.org/
  • Owner / steward: Pharo Consortium (Inria, RMOD, Lille; CNRS) for Pharo. Squeak Foundation for Squeak. Instantiations (commercial, formerly OTI/IBM) for VAST. GemTalk Systems for GemStone/S.
  • Paradigms: Pure object-oriented. Everything is an object — including classes, methods, blocks (closures), and the IDE itself. Message passing is the only computational primitive.
  • Typing: Dynamic, strong, latent. No static type declarations. Method dispatch is runtime by selector + receiver class.
  • Memory: GC’d. Modern Pharo uses a generational scavenger + incremental mark-sweep; the Spur object format (Eliot Miranda, 2014+) is the modern object memory used by Pharo and Squeak (https://clementbera.wordpress.com/2014/01/16/spur-a-new-object-representation-for-cog/).
  • Compilation: Source compiled to bytecode, executed by the Cog VM (a JIT VM written in itself via the Slang/VMMaker bootstrap; Eliot Miranda is the principal author). The VM lives in C generated from Smalltalk.
  • Primary domains: Live programming, exploratory data analysis, domain modeling, software visualization (Roassal), education, business systems (GemStone/S in finance and healthcare), heavy multi-user object servers.
  • Official docs: https://pharo.org/https://books.pharo.org/https://squeak.org/ • ANSI Smalltalk Standard (paywalled).

At a glance

Smalltalk is the live-image language. There is no “build → run → debug” loop because there is no separate build artifact: you work inside a running image where every object — including the compiler, the debugger, the windowing system, and your own classes — is editable while running. Pharo is the actively developed, modern, MIT-licensed flagship; Squeak is the original-PARC-team lineage (Kay, Ingalls, 1996); GemStone/S is a commercial multi-gigabyte persistent object database that’s also a Smalltalk runtime; VAST is the IBM/OTI commercial line; Cuis is a minimalist Squeak fork by Juan Vuletich. Sources: https://pharo.org/, https://squeak.org/, Goldberg & Robson, Smalltalk-80: The Language and its Implementation (Addison-Wesley, 1983).

Getting started

  • Install (Pharo): The Pharo Launcher is the canonical entry point — download images and VMs, manage multiple Pharo versions side by side. https://pharo.org/download

    # Or via Zeroconf:
    curl https://get.pharo.org/64/130+vm | bash
    ./pharo Pharo.image
  • No version manager analog needed: images are self-contained directories (<name>.image, <name>.changes, <name>.sources); copy to fork.

  • Hello world (Pharo Playground, Cmd-Shift-O):

    Transcript show: 'Hello, Smalltalk!'; cr.

    Or, evaluate inline (Cmd-P, “Print it”): 'Hello, Smalltalk!'.

  • Project layout: Source code lives in the image, not in files. The mapping to filesystem is done by Tonel format (Pharo’s git-friendly textual format — one method per file is not used; one class per file with method chunks): https://github.com/pharo-vcs/tonel.

  • Package & build tool: Metacello is the dependency manager (declares baselines and configurations); Iceberg is the git integration (https://github.com/pharo-vcs/iceberg).

    Metacello new
      baseline: 'PetitParser2';
      repository: 'github://kursjan/petitparser2';
      load.
  • REPL / live env: The Playground (scratchpad), System Browser (class navigation), Inspector (live object exploration with custom views via Phlow), Debugger (step + edit + restart on the fly), Spotter (Cmd-Shift-F, fuzzy search across everything).

Basics

  • Types & literals: Everything is an object. 42 (SmallInteger), 1/3 (Fraction), 3.14 (Float), $a (Character), 'hello' (String), #symbol (interned Symbol), #(1 'two' $3) (literal Array), nil, true, false, [:x | x + 1] (block / closure / BlockClosure).

  • Variables / scoping: | x y | declares temporaries. Class-side instance variables, class variables, pool dictionaries, globals (Smalltalk globals).

  • Control flow: All “syntax” is messages. cond ifTrue: [...] ifFalse: [...], [cond] whileTrue: [...], 1 to: 10 do: [:i | ...], coll do: [:e | ...]. There is no if/while/for keyword.

  • Methods: Three syntactic forms — unary (obj negated), binary (a + b), keyword (dict at: 'k' put: 'v'). Method definition lives in a class:

    Integer >> factorial
      ^ self <= 1 ifTrue: [1] ifFalse: [self * (self - 1) factorial]

    ^ returns from the method; without it, the method returns self.

  • Strings: Mutable String (immutable variants exist). 'a' , 'b' for concat; 'foo' asSymbol to intern.

  • Collections: Rich hierarchy: Array, OrderedCollection, Set, Dictionary, Bag, LinkedList, Interval, Stream. Polymorphic protocol — do:, collect:, select:, reject:, inject:into: (fold), detect:, count: work across all of them.

Intermediate

  • Class system: Single inheritance + traits (Pharo). Every object has a class; every class is also an object (instance of its metaclass). The metaclass hierarchy mirrors the class hierarchy. Object class superclass = Object class is not — read https://gbracha.blogspot.com/2008/12/inheritance-versus-subtyping.html and the Pharo book.
  • Modules / packages: RPackage (Pharo) groups classes; BaselineOf<Name> defines a project’s structure for Metacello. Tonel format for git.
  • Error handling: Exceptions are first-class objects. [risky] on: ZeroDivide do: [:ex | ex return: 0]. ex pass, ex signal, ex retry give fine control. Resumable exceptions are real — ex resume: value continues from the signaling point.
  • Concurrency: Cooperative green threads (Process), preempted by priority. Synchronization via Semaphore, Mutex, Monitor, SharedQueue. The whole image is a single OS thread by default; TaskIt (https://github.com/pharo-contributions/taskit) provides futures and promise composition. Pharo 13 ships an FFI threading API for native off-image work.
  • I/O: FileSystem (filesystem path/file abstraction), ZnClient (HTTP via Zinc), OSSubprocess (subprocess), Stream hierarchy. Transcript is the system console.
  • Stdlib highlights: Kernel, Collections, Files, Streams, Network (Zinc HTTP, Soup HTML parser), Tools (System Browser, Inspector, Debugger), Spec2 (UI builder), Bloc (vector graphics), Roassal3 (visualization), NeoJSON/STON (serialization).

Advanced

  • Memory / GC: Spur object format (one object header, indirect class refs via class table, segmented old space). Generational + mark-sweep + compactor. Pharo can grow/shrink the heap dynamically. Inspect with Smalltalk garbageCollect, Smalltalk vm parameters. https://clementbera.wordpress.com/

  • Concurrency: As above — green threads + the FFI thread bridge. The single-image-thread model is a real constraint; for compute parallelism, spawn child Pharo images via OSProcess or use GemStone for distribution.

  • FFI: uFFI (Pharo’s modern FFI, by Esteban Lorenzano):

    LibC >> abs: anInteger
      ^ self ffiCall: #(int abs(int anInteger)) module: 'libc'

    Cleaner than Squeak’s older NBFFI. https://github.com/pharo-project/pharo/wiki/uFFI-tutorial

  • Reflection (the MOP): Smalltalk has the fully reflective Meta-Object Protocol. From any object you can:

    • inspect class: obj class, obj class methodDictionary, obj class instVarNames.
    • read/write instance variables: obj instVarNamed: 'foo', obj instVarNamed: 'foo' put: 42.
    • get the live execution context: thisContext, thisContext sender, walk the stack.
    • intercept every failed dispatch: override Object >> doesNotUnderstand: aMessage (DNU) — basis for proxies, futures, mock objects, RPC, ORM lazy loaders.
    • swap one object’s identity for another atomically: oldObj becomeForward: newObj rewrites every pointer in the image.
  • Performance tools: MessageTally (sampling profiler — MessageTally spyOn: [...]), TimeProfiler, SpaceTally. The Iceberg UI shows hotspot annotations.

  • Tactics / proof: N/A. Smalltalk has no static analyzer / theorem prover in the box, but the live debugger is a kind of “operational theorem prover” — change a method, restart the call frame, observe.

God mode

  • The image as a database: The .image file is a memory snapshot. Save it (Smalltalk snapshot: true andQuit: false) and you have your entire dev environment, open windows and all, frozen for resume. The hardest problem in Smalltalk is source control on this binary blob — solved (uneasily) by Tonel + Iceberg. https://github.com/pharo-vcs/iceberg

  • doesNotUnderstand: (DNU): Any class that overrides doesNotUnderstand: becomes a proxy for any message. Idiomatic for futures, transparent persistence, mocks, lazy ORM:

    RemoteProxy >> doesNotUnderstand: aMessage
      ^ self transport send: aMessage selector args: aMessage arguments
  • become: and becomeForward:: Atomic identity swap. Every reference to the receiver is repointed to the argument (or vice versa) by a heap walk. Used for hot-class reshape (adding instance variables to a live class without restarting), schema migration in GemStone, and the IDE’s own “edit a class while instances of it exist” magic.

  • Metaclass hierarchy: Each class is an instance of its metaclass. Integer class is Integer class; the metaclasses form a parallel hierarchy. Class-side methods (defined “on the class side”) are instance methods of the metaclass.

  • thisContext: A pseudo-variable that evaluates to the currently executing method context — the activation record, as a real object. Walk it (sender, method, pc, tempAt:) for stack traces, continuations, call/cc-style control flow, and the debugger.

  • Reflective MOP — rewrite the compiler from inside: Compiler is itself a Smalltalk class with editable methods. You can install a different parser (Pharo’s OpalCompiler is the default, swappable). People have prototyped new languages by replacing only the compiler and keeping the rest of the image. https://opalcompiler.gitbook.io/

  • Pragmas + method properties: <menuItem: 'Inspect'> annotations on methods, queryable at runtime — power for everything from menu construction to declarative test runners.

  • Spec / Spec2: Declarative UI builder with separate model/view/controller. Composes via subclasses, supports MVP-style presenters. https://github.com/pharo-spec/Spec

  • Iceberg (git integration): Iceberg makes git work with Tonel. Each method-edit becomes a textual diff, but the in-image edit experience is preserved. Branch switching reloads classes live. The hardest part of Smalltalk in 2025 is still git, and Iceberg is the practical answer.

  • Bloc + Brick: Pharo’s modern vector graphics framework (replacement for the old Morphic). https://github.com/pharo-graphics/Bloc

  • Roassal: Visualization framework — interactive charts, graphs, code maps. Trivial to wire to live image data. https://github.com/pharo-graphics/Roassal

  • Live (debug-driven) programming: Run a test that fails → debugger opens at the failure → write the missing method in the debugger → click “Proceed” → test goes green. The “save the world from a debugger” workflow is documented in Pharo by Example.

  • Slot model + first-class slot reflection: Pharo replaced raw instance variables with Slot objects — each variable is an object that can intercept reads/writes (e.g., property bindings, computed slots, validation). https://github.com/pharo-project/pharo/wiki/Slots

  • Phlow: Custom inspector views per class — open an inspector on a ZnUrl, see “Components” / “Resolved” / “HTTP Headers” tabs you defined.

  • Microdown: Pharo’s lightweight markdown variant for in-image documentation, with executable code snippets that round-trip through the image.

Idioms & style

  • Naming: UpperCamelCase for classes (OrderedCollection); lowerCamelCase for selectors (addFirst:, at:put:); keyword selectors are one selector with multiple parts (at:put: is one method name).
  • Formatter / linter: Pharo ships RBFormatter (Refactoring Browser formatter) and a Quality Assistant that flags smells in real time. Uniform indentation: 1 tab per level, period at end of statement (not last in block).
  • Idiomatic patterns:
    • “Tell, don’t ask” — push behavior into the receiver class.
    • Polymorphism over isKindOf: checks.
    • Cascades (;) for method chains on one receiver: OrderedCollection new add: 1; add: 2; yourself.
    • Blocks for control flow (ifTrue:, whileTrue:) — all control is messages.
    • Avoid class checks; use double dispatch.
  • What experts look for: Small methods (Smalltalkers aim for ≤ 7 lines, single responsibility); meaningful selectors that read like English; refactoring browser (Cmd-T) used heavily; tests written in SUnit (the original xUnit, https://en.wikipedia.org/wiki/SUnit); use of assert: / deny: rather than control-flow assertions.

Ecosystem

  • Pharo libraries:
    • Zinc — HTTP client/server.
    • NeoJSON / STON — JSON / Smalltalk Object Notation.
    • Voyage — object-document mapping (Mongo, in-memory).
    • Glamorous Toolkit (GT) — moldable development environment built on Pharo (https://gtoolkit.com/), arguably the most innovative Smalltalk descendant in active use.
    • Seaside — continuation-based web framework (https://seaside.st/).
    • TaskIt — futures.
    • PetitParser — parser combinators.
    • OSSubprocess — subprocess management.
  • Squeak ecosystem: Etoys (kid-friendly programming env, used in OLPC), Croquet/OpenCobalt (collaborative 3D), Scratch (originally built on Squeak before its rewrite).
  • Other Smalltalks:
    • GemStone/S — multi-user persistent OODBMS Smalltalk (https://gemtalksystems.com/), used in finance/healthcare.
    • VAST Platform (formerly VisualAge) — Instantiations, commercial, https://www.instantiations.com/.
    • Cuis Smalltalk — minimalist Squeak fork by Juan Vuletich (https://cuis.st/).
    • Amber Smalltalk — Smalltalk that compiles to JS, browser-runnable.
    • Dolphin Smalltalk (Object Arts) — Windows-native, open-sourced 2016.
  • Notable projects: Croquet (later Open Cobalt), Scratch 1.x (built on Squeak), GemStone/S in production at NYSE-derivative trading desks, Newspeak (Gilad Bracha — Smalltalk-influenced; was Smalltalk-implemented).

Gotchas

  • Image-based workflow is the philosophy and the problem: if you Smalltalk snapshot with a broken image, your dev env is broken until you load a backup. Always commit and keep the previous .image around.
  • Git is hard: Tonel + Iceberg are good but not seamless. Merge conflicts on method-level chunks happen. Branch-per-feature with frequent commits is the safe path.
  • Single-VM-thread: A blocking FFI call freezes the whole UI. Use OSSubprocess for blocking IO; use uFFI’s threaded variant when available.
  • No type system: Refactors that would be safe in Java/Haskell can break callers silently. SUnit coverage is the only safety net — invest heavily.
  • become: is dangerous and slow: It walks the heap. Don’t call it in tight loops.
  • Reflection makes things possible that probably should not be done: doesNotUnderstand: proxies can hide real bugs (typos in selector names look like proxy hits). Use sparingly.
  • DNS-of-source: A method’s source code is stored in .changes / .sources; corrupting these makes “view source” return <source missing> even though execution still works (bytecode is in the image).
  • VM/image version skew: A Pharo 13 image needs a Pharo 13 VM. The Pharo Launcher prevents most mistakes; manual installs do not.
  • ANSI standard ≠ what’s in the image: The 1998 ANSI Smalltalk standard is a baseline; every implementation extends it differently. Portable code uses only the ANSI subset, which is small.
  • Squeak vs Pharo divergence: Pharo aggressively cleaned up Smalltalk-80 inheritance for modernity; many Squeak idioms (Morphic UI, certain class names) differ. Don’t assume cross-compat.
  • Cmd shortcuts vs Ctrl: macOS uses Cmd, others use Ctrl/Alt — older docs assume macOS bindings.

Citations