Lisp source code is executed in two phases:
- The source is read by the reader, which parses the source into a data structure.
- Then the evaluator traverses this data structure, interpreting the data into action according to a simple set of rules.
While the source of just about all languages is processed by first translating it into a data structure---an Abstract Syntax Tree (AST)---Lisp is unique in that it keeps this data structure and the evaluation rules simple enough for you, the programmer using the language, to understand the details of this process in full. Once you understand how the reader and evaluator "think", you'll understand Lisp.
Like other dynamic languages, Lisp can be used in an interactive command-line mode. Lisp calls this mode the Read Evaluate Print Loop (REPL): each time a command is typed at the REPL, it is read and then evaluated, and then the value returned by evaluation is printed. The REPL is useful as a calculator, for quick experiments, or for inspecting or modifying a running program (such as when a break is triggered when debugging).
Programs, however, are of course written as source code files. You might expect the reader to read a whole source file up front before handing off to the evaluator, but this is not the case: in some Lisp dialects, the behavior of the reader can be modified by evaluated code, so the reader hands off each chunk of usable code to the evaluator as it reads them, thereby allowing code to modify how code below it will be read. In Clojure, the reader's behavior is currently not modifiable, but Clojure follows this traditional read-evaluate pattern anyway. (In the future, Clojure may allow reader modification.)
In Clojure, the reader ignores ";" and everything after it on the line.
; this is a comment
Whitespace is required to separate "atoms" (symbols, keywords, number literals, and string literals, etc.) from each other but is otherwise ignored. The , character is considered whitespace, which is useful stylistically for creating a clearer visual separation between atoms:
foo, bar ; the comma is treated the same as if it were a space
But in string literals, of course, whitespace, ;, and , are treated as themselves.
Otherwise, the reader basically sees source as a bunch of literals. For instance:
(def jerry [1 2 3])
The reader parses this into a list containing the symbol def, then the symbol jerry, and last a vector with the Integers 1, 2, and 3. Because this list is at the "top level" of code (it isn't contained in another collection), it is passed off to the evaluator immediately once it is fully read.
The behavior of the reader can be modified by special prefixes on literals called reader macros, but these are pretty much just convenience features, so we'll discuss them later.