LISP on the IBM 704: Turning Symbolic Expressions into Computation
How John McCarthy's recursive-function model and the early IBM 704 LISP system made symbolic expressions executable data.
LISP began as a response to a research problem: how could a computer manipulate symbolic expressions and support experiments in artificial intelligence? John McCarthy’s 1960 paper, “Recursive Functions of Symbolic Expressions and Their Computation by Machine,” presented a mathematical framework and described a LISP system developed for the IBM 704. The result connected recursive definitions, symbolic data, and machine computation in a way that made programs easier to express as structures the computer could inspect and transform.
LISP is often summarized as a language whose code is data. That slogan points to an important design feature, but the original system deserves a more exact account. McCarthy’s paper distinguished mathematical functions over symbolic expressions from the implementation of a practical programming system. The early IBM 704 manual then documented a working programming environment with its own machine constraints. LISP was not born as a finished, machine-independent language; it grew from theory and an implementation aimed at a specific computer.
A language for symbolic computation
Most early programming examples emphasized numeric calculations: add numbers, process arrays, and print results. McCarthy wanted a system for manipulating expressions that represented formal statements and could support the proposed Advice Taker, a system intended to reason from declarative and imperative information. This required a convenient representation of symbolic structures, not only fixed-size numbers and character strings.
The paper appeared in Communications of the ACM in April 1960. McCarthy describes the LISP system as developed for the IBM 704 by an MIT Artificial Intelligence group. Its purpose was to support experiments with a system that could use symbolic descriptions and make deductions. The paper’s title says “Part I” because it focused on recursive functions and their computation; it was not a broad product manual for every feature of later Lisp dialects.
The central data structure was a symbolic expression, or S-expression. Lists could represent data, expressions, and nested relationships. An illustrative expression in later common notation might be:
(PLUS X (TIMES Y 3))
This example shows a tree-like structure: a top-level operation contains another operation as one of its arguments. It should not be mistaken for a verbatim IBM 704 program from the original 1960 manual. The original notation and interpreter details differ from modern Common Lisp and Scheme, but the core idea of representing nested symbolic expressions as lists is visible in the early papers.
Recursive functions gave the language its computational shape
McCarthy used recursive definitions to express functions over symbolic expressions. A function could inspect whether an expression was atomic or compound, select its components, and recursively process those components. This made tree traversal a natural programming task rather than a sequence of manually calculated memory offsets.
The paper’s formalism introduced operations for selecting parts of expressions and constructing new ones. In Lisp family terminology, CAR and CDR retrieve components of a pair, while CONS constructs a pair. These names came from fields used in IBM 704 machine instructions and were retained as programming-language operators. A list can be understood as a chain of pairs ending in an empty-list marker, although the physical representation on the 704 is not identical to the pointer model most modern programmers imagine.
Recursion matched the nested structure of symbolic data. To process a list of expressions, a function could process the first element and then process the rest. To evaluate a compound expression, an evaluator could recursively evaluate its operator and arguments according to rules. The same general program structure could work on expressions of different shapes without first declaring every possible record layout.
The paper’s eval function became especially influential as a compact description of evaluation. A program could define how atomic expressions and compound expressions should be interpreted. Later Lisp systems popularized the idea that evaluation rules could be expressed in Lisp itself. That does not mean all early Lisp systems implemented every modern reflective feature in the same way; the original publication presents a formal system and implementation details tied to its research context.
Why the IBM 704 mattered
The IBM 704 was a scientific computer with hardware features, word formats, and instruction conventions that shaped the implementation. The LISP programmer’s manual documents its target machine and system. The symbolic representation had to map onto 704 words and operations. The implementation used machine-level support for constructing and traversing lists, and the primitive names reflected the underlying instruction environment.
This is a critical distinction between language abstraction and machine implementation. Programmers could reason in terms of symbolic expressions, but the runtime still needed concrete memory layouts and operations to find list components. A list-processing system had to allocate storage, represent atoms, compare symbols, and reclaim or reuse memory as programs generated new structures. The paper’s formal notation did not make the 704 disappear; it gave programmers a new layer over the hardware.
The implementation itself evolved through stages. McCarthy’s paper recounts changes from an earlier M-expression syntax toward S-expressions, where the symbolic expression representation could also serve as a readable program notation. This convergence made the language easier to implement and provided a uniform way to represent both code and data. It is an early example of a design becoming simpler when the data model and expression syntax align.
The IBM 704’s word-oriented architecture also meant that a program had to bridge the representation of symbols with available memory and instruction formats. It would be misleading to assume modern 64-bit tagged pointers or a contemporary garbage collector when reading a 1960 implementation. The right procedure is to distinguish mathematical definitions in the paper from storage details in the LISP I manual and from later Lisp-family conventions.
The 1960 manual shows a working system
The Computer History Museum’s software preservation archive holds a scan of the LISP I Programmer’s Manual dated March 1, 1960. Its sections document the system’s use of symbolic expressions and provide operational information for programmers. The manual is valuable because it captures more than a language manifesto: it shows an implementation environment with a particular machine, compiler or interpreter tools, and user workflow.
The manual also reveals the gap between a compact mathematical idea and a usable system. Programmers needed to know how to write functions, load programs, interact with the environment, and diagnose errors. A language definition alone does not provide these services. The team had to build system routines and machine-specific conventions that allowed symbolic programs to run on an IBM computer center.
Early LISP was a research system, not a widely distributed commercial package with a modern ecosystem. The documentation served specialists experimenting with symbolic computation and artificial intelligence. Later systems, including Lisp 1.5 and numerous dialects, revised syntax, libraries, compilers, and runtime systems. Some operators survived; some details changed. A later dialect manual cannot automatically serve as evidence for what LISP I did in 1960.
Programs as data, with an important qualification
When a Lisp program is itself represented using symbolic expressions, software can inspect, transform, generate, or evaluate program structures. This makes macro systems and metaprogramming natural directions for later Lisp languages. But the phrase “code is data” should not obscure evaluation rules, variable binding, environments, side effects, and compiler behavior. A list that looks like source code is not always executable until the language assigns it meaning.
In early LISP, the shared symbolic representation was valuable for research because the program could manipulate expressions that resembled the formulas and knowledge structures being studied. It made transformations explicit. A symbolic algebra routine could inspect operators and operands; a theorem-proving program could construct and compare logical expressions; an evaluator could apply functions to expression trees.
The same flexibility made memory management challenging. Recursive algorithms generated many temporary structures. Implementations needed strategies for reclaiming storage or reusing nodes. As Lisp systems matured, garbage collection became closely associated with dynamic symbolic computation, but one must check the exact implementation and version before ascribing a particular collector design to LISP I. The high-level data model creates memory-management obligations, not a single inevitable collector algorithm.
From research language to a family of languages
LISP influenced later programming languages and AI research because it joined recursion, symbolic data, and interactive experimentation. The system enabled researchers to build programs whose input and output were structured expressions. Its syntax made nesting visible and its operators made list transformations composable. Those ideas would appear in later Lisp dialects and in broader programming-language research.
The language did not remain unchanged. Lisp 1.5, Maclisp, Scheme, Common Lisp, and other descendants made distinct choices about evaluation, types, compilation, environments, and libraries. A modern defun, macro, lexical closure, or package system should not be read backward into the first LISP manual without evidence. The lineage is continuous but not identical.
LISP’s importance is also not confined to AI. Symbolic manipulation is useful in compilers, theorem provers, algebra systems, editors, and any application that treats structured expressions as data. The original motivation was a proposed reasoning system, but the underlying list-processing model proved more general than one application.
How to read the original record
The most reliable reconstruction separates three artifacts. McCarthy’s 1960 paper explains the formal ideas and early motivation. The LISP I Programmer’s Manual shows how the working system exposed those ideas to users. The IBM 704 documentation provides the target machine context. Together they prevent two common errors: presenting LISP as a timeless abstract language with no hardware constraints, or reducing it to machine-specific code with no conceptual contribution.
The evidence supports a focused conclusion. LISP on the IBM 704 made symbolic expressions directly useful as structures for recursive computation. The system emerged from an effort to explore machine reasoning, and its list representation helped unify programs and data. The implementation was tied to a real machine and changed over time, but the combination of recursive definitions and symbolic structures became one of computing’s durable design patterns.
Related:
- FORTRAN and the IBM 704: Making High-Level Numerical Code Run Fast
- How to Explore Historically Significant Source Code Directly
Sources: