From time to time, we see lisp very newbies try to write "blocks" of expressions within parentheses like:
((expression1) (expression2))
on the model, it is assumed, of C:
{statement1; statement2;}

This is not the way of Lisp.

Hey newbie! Here is the secret:

Lisp has no syntax. Lisp has no parentheses. Lisp has no lists.

Well, it has, some, but not at the same level the other languages have or don't have them.

Lisp has no syntax.

Indeed, since the sources are but syntactic trees, there's no concrete syntax for lisp. Or at least, not a single one. We tend to write nodes of these trees with the "list" syntax: '(' item ... ')', but actually there are a lot of other ways to build this syntactic tree (DAG actually), including various reader or normal macros.

But inside a program, There is no syntax, eg. for a complex number, no #c(1.2 3.4); there are only complex numbers, with values such as 1.2+3.4*i.

Lisp has no parentheses.

Indeed, just try it:

(map nil (function print) '(a b c))

--> NIL
See? 0 parentheses in (a b c).

In lisp _sources_, there are no parentheses. Of course, actually there are parentheses at the level of the characters reads by the standard lisp reader. But they could very easily disappear:

(set-syntax-from-char #\[ #\()
(set-syntax-from-char #\] #\))
(set-macro-character #\[ (lambda (stream char) (read-delimited-list #\] stream t)))
Et hop! No parentheses anymore:
[map nil [function print] '[a b c]]

--> NIL

Or we could write a macro or a parser, that would generate some code with a lot of tree nodes, that could be printed by the standard lisp printer with a lot of conventional parentheses, but which wouldn't contain any parenthese in the source (of the macro, or the parsed text).

Lisp has no lists.

Again, just ask it:

(type-of '(a b c)) --> CONS
(type-of '())      --> NULL
See? No type LIST here. List is not a primitive lisp type. The primitive non-atomic Lisp type is the CONS cell. And lists are but a pun on cons cells, at a higher level of abstraction.

Above the level of the CONS cells, we build an abstraction named LIST. So we define FIRST = CAR, REST = CDR, ENDP = NULL, and LIST = (CONS ... (CONS ... '())).

But above this level of "lists", that are actually just cons cells, we build an abstraction named syntactic tree. So we define: OPERATOR = FIRST; ARGUMENTS = REST; EMPTYP = ENDP and MAKE-NODE = CONS.

OPERATOR being the _label_ of the tree node, and

ARGUMENTS being the _children_ of the tree node.

Lisp sources

The _sources_ of a lisp program are just syntactic trees, at a higher level of abstraction relatively to the textual input that may (or may not) be used to build the tree.

Of course, at this higher level, there is some 'syntax'. After all, they're called syntactic trees. But it's an "abstract" syntax, epured from mundane details such as what character to put here or there. What remains is only the label of the node, the operator, and an ordered list of children, the arguments.

       label: +      children: 1 2 3
       label: IF     children: <condition> <then-clause> <else-clause>
       label: PROGN  children: <expression>...

You need to know the order of the children, what child to put at what position, because this is what is significant. Happily, there are some conventions (e.g. in the order of the arguments to macros, we usually put the name of the thing declared, or the name of the variables first, while the body or the other variable-in-number clauses come last), and there are some tools like keyword arguments that allow one to get free from this ordering constraint.

We don't need, and it would be ridiculous from the point of view of list, to specify a grammar rule with a pair of characters and a syntax like in C:

     block ::= '{' { statement ';' } '}' .
or even, to specify some BEGIN and END tokens like in Pascal:
     block ::= 'BEGIN' [ statement { ';' statement } ] 'END' .
(note the artificial difference in _syntax_! Blocks, both in Pascal and in C are parsed the same, into a node of a syntactic tree such as:
      label: BLOCK  children: statement...
and this is all that matters. Well, in lisp, we just put:
      label: BLOCK  children: statement...
in the source.

For example, by way of the READER (and therefore, using the characters to which the reader macro to build symbol lists is bound to):

(map nil (function print)
         (read-from-string "(BLOCK statement1 statement2 statement3)"))

--> NIL

But we could also build the syntactic tree node by way of an expression:

(map nil (function print)
         (cons 'BLOCK  (append (list 'statement1 'statement2)
                               (cons 'statement3 '()))))

--> NIL

Categories: Online Tutorial