Read- vs Run-time

You must strictly separate READ from EVAL.

Lisp is a REPL, a Read/Eval/Print-Loop. The "Print" only happens on the top level, so what we actually have is a loop which READs expressions and EVALuates them.

While reading, no evaluation takes place. It is just a conversion of human-readable text to internal cell structures.

For example, Lisp-code like (mapc println (1 2 3)) is directly interpreted. This sequence of parentheses and characters like 'm', 'a', 'p' etc. are just a representation of the internal s-expr structure. The reader converts it to:

   +-----+-----+     +-----+-----+     +-----+-----+
   |  |  |  ---+---> |  |  |  ---+---> |  |  |  /  |
   +--+--+-----+     +--+--+-----+     +--+--+-----+
      |                 |                 |
      V                 V                 V
    mapc             println           +-----+-----+     +-----+-----+     +-----+-----+
                                       |  1  |  ---+---> |  2  |  ---+---> |  3  |  /  |
                                       +-----+-----+     +-----+-----+     +-----+-----+

and, when printed again, it shows up as (mapc println (1 2 3)). The tokens 'mapc' and 'println' here denote references to the cell structures of those symbols.

Read-Macros extend this, or mix it up. When, while reading an expression character by character, the interpreter sees a backquote or a tilde, it 'read's the following expression recursively, calls EVAL on it, and uses the result instead of what it read.

An example

In most programs, you don't have just two "times" (read and run). Sometimes there are many levels involved. Consider this:

   (setq CONST 7)

   (de foo (Lst)
         '((X) (bar (mumble (+ X `(* 2 CONST)))))
         Lst ) )

First (setq CONST 7) is read, and evaluated, calling the function 'setq'. As a side-effect, the symbol CONST is set to 7.

Then the second expression is read. This causes the read-macro

   `(* 2 CONST)

to be evaluated, so the reader returns this list:

   (de foo (Lst) (mapcar '((X) (bar (mumble (+ X 14)))) Lst))

This list is evaluated, causing the function 'de' to run. As a side-effect, the value of 'foo' is set to the list

   ((Lst) (mapcar '((X) (bar (mumble (+ X 14)))) Lst))

Then, at some LATER time, 'foo' is called. We might call it the third level. This causes the function 'mapcar' to be called, which receives as its first argument the list

   ((X) (bar (mumble (+ X 14))))

Then, again a little later, 'mapcar' calls this list as a function.

Another example

There are even more levels this GUI fragment:

      (html 0 "Test" "lib.css" NIL
         (<h3> NIL "Test")
         (form NIL
            (gui '(+Button) "Dialog"
               '(dialog NIL
                  (gui 'txt '(+Init +TextField) (val> (: top 1 txt)) 12)
                  (okButton '(msg (val> (: home txt))))
                  (gui '(+Button) "Alert"
                     '(alert NIL "Bla bla bla"
                        (okButton) ) )
                  (cancelButton) ) ) ) ) )

1. When the browser arrives at this page, this file is loaded. 'load' reads and evaluates all expressions in the file. So the list (action ...) is read.

2. The list is evaluated: Calling (action (html ...)) builds a form data structure, and sends HTML code back to the browser. In that course the 'gui' function is called and builds a button component labeled "Dialog".

The list (dialog NIL ...) is stored in the button, but not yet evaluated.

3. If the user clicks on the button labeled "Dialog", the 'dialog' function runs. It builds a dialog data structure, and sends more HTML code back to the browser. Again the 'gui' function runs, and builds another button "Alert" in the dialog.

The list (alert NIL "Bla bla bla" ..) is stored in THAT button, but not yet evaluated.

4. If the user clicks on THAT button, labeled "Alert", then that list is evaluated, and an alert pops up.

So you see, we have many different times of execution in a single expression. This can go on ad infinitum.

09apr17   erik