← back to programming

Multiple Representations

Abelson & Sussman ยท 1996 ยท SICP ยง2.4

The same abstract data can have multiple concrete representations. Tagged data and dispatch tables let representations coexist without knowing about each other. This is the birth of extensibility: adding a new representation requires no changes to existing code.

rectangular polar real-part magnitude car z r cos θ √(x²+y²) car z dispatch table: operations × types → implementations

Representations for complex numbers

A complex number can be stored as rectangular (real, imaginary) or polar (magnitude, angle). Both are valid. The operations real-part, imag-part, magnitude, angle define the abstract interface. Two teams can implement independently.

Scheme

Tagged data

Problem: both representations are just pairs. How does a generic operation know which one it's looking at? Answer: attach a type tag. (attach-tag 'rectangular (cons 3 4)) produces (rectangular 3 . 4). The generic operations dispatch on the tag.

Scheme

Data-directed programming

The conditional dispatch above doesn't scale: adding a new representation means editing every generic operation. Data-directed programming uses a two-dimensional table indexed by (operation, type). Each representation installs its own procedures. The generic operation just looks up the table.

Scheme

Message passing

Instead of a central dispatch table, each object carries its own dispatch. You "send a message" (the operation name) to the object, and it returns the right value. This is what object-oriented programming looks like from first principles: an object is a closure that dispatches on a symbol.

Scheme

Notation reference

Scheme Python Meaning
(attach-tag 'type data)dict(type=..., ...)Tag data with its type
(type-tag datum)datum.get('type')Extract the type tag
(contents datum)datum (minus type key)Extract the payload
(put op type proc)dispatch[(op, type)] = fnInstall in dispatch table
(get op type)dispatch.get((op, type))Look up dispatch table
(apply-generic op datum)apply_generic(op, datum)Dispatch on type tag

Translation notes

The three dispatch strategies — explicit conditional, dispatch table, message passing — correspond directly to patterns in mainstream OOP. Explicit conditionals are isinstance checks. Dispatch tables are the visitor pattern. Message passing is virtual method dispatch. Python classes combine all three: isinstance for ad-hoc checks, __init_subclass__ for registry patterns, and regular methods for message passing.

SICP's insight is that these are all the same mechanism (dispatch on type) organized differently. Data-directed programming is the most extensible: new types and new operations can be added independently. This is exactly the expression problem that haunts every language design.

Neighbors

SICP chapters

Foundations (Wikipedia)

Ready for the real thing? Read SICP §2.4.