I started to learn lisp recently. I was going over the examples in the book Land of Lisp and I felt that I was understanding everything rather well until I arrived to the following example around chapter 7:
(defun quote-it (x)
(list 'quote x))
Now, I know that list
would create a list with its arguments, like in (list 1 2 3 4)
would evaluate to the list (1 2 3 4)
.
And I also know that quote
allows me to quote an argument, pretty much like I can do with '
. And so 'east
is the same as (quote east)
Now, interestingly, the function above does not return a list, but simply quotes whatever I pass to it. And so, if I call it
(quote-it east)
it would simply return 'east
and not ('east)
If I had done this, I would have written the function as
(defun quote-it (x)
(quote x))
And so, I have no clue why we write the command as 'quote
in (list 'quote x)
in the example from the book.
I know I can switch between code and data by using quotes, like '(+ 1 2)
, but in this case it looks like my intention is to actually apply the quote function here. So, why (list 'quote x)
?
So, can somebody out there with more experience clarify this?
Lisp source code is represented by some of the same data structures that are used in Lisp programming. Of particular importance here are lists and symbols. When Lisp evaluates a form that has the form
(quote something)
that is to say, when Lisp evaluates a form which is a list and whose first element is the symbol quote
, then it returns the object something
without evaluating it. Thus
(quote 2) ;=> 2
(quote (a b c)) ;=> (a b c)
Now, that's what the evaluator (or compiler, &c.) do when they get a Lisp object to evaluate that happens to be a list whose first element is the symbol quote
. As Lisp programmers, we still have to write code for the Lisp reader to read and pass to the evalutor. We can write the long forms
(quote 2)
(quote (a b c))
in our source, and the Lisp reader will read them and pass them to the compiler, but we're virtuous programmers, so we're lazy, and want to avoid some typing. So, we can type
'2
'(a b c)
instead. The compiler ends up getting exactly the same input; a list whose first element is the symbol quote
and whose second value is 2
or (a b c)
.
Now we can talk about your code. The form
(list 'quote x)
returns a list whose first element is the symbol quote
, and whose second element is the value of the variable x
. That can be printed as
(quote <value-of-x>) ; fill in x's value for <value-of-x>, of course
Now, the Lisp printer is a bit clever, and has the option of printing things like that using the same shorthand that we're allowed to use when we're writing source. So that can also be printed as
'<value-of-x>
Now we've got enough to address your question about the book's code:
Now, interestingly, the function above does not return a list, but
simply quotes whatever I pass to it. And so, if I call it
(quote-it east)
it would simply return 'east
and not ('east)
Since 'east
is an abbreviation for (quote east)
, we now see that (quote-it east)
does, in fact, return a list. And it's a list of the form you'd expect: a list whose first element is the symbol quote
and whose second element is the symbol east
. If it had returned ('east)
, it would still be returning a list, but it would be returning a list of the wrong form. ('east)
is, when we expand the abbreviation, the list ((quote east))
; that is, it's a list of a single element, and that element is another list whose first element is the symbol quote
and whose second element is the symbol east
. It's certainly a list, but it's not the list you were looking for.
Now we can take a look at your proposed code. Your function quote-it
only incidentally happens to work in the case that you're calling (quote-it 'x)
. That is, you can do
(quote-it 'x)
;=> x
but notice that you're getting a symbol back, and you're getting the symbol x
back. You're not getting a list whose first element is the symbol quote
and whose second element is <value-of-x>
. When you use quote-it
with other values, you'll see the problem:
(quote-it 2)
;=> x
You still get a symbol back, because
(defun quote-it (x)
(quote x)) ; or 'x
Takes an argument, binds it to the lexical variable x
, and then returns the value of (quote x)
. What's the value of (quote x)
? As explained earlier, when the evaluator (or compiler, &c.) gets a list of the symbol quote
and something
, the value is the literal something
. Since the body of quote-it
is a list of the symbol quote
and the symbol x
, the value of the body of quote-it
is the symbol x
.
Example: If you want to construct a list with a symbol foo
and a variable number:
- we have a number
- we want to make a list
(foo <some-number>)
Now we write a function for it:
(defun foo-it (n)
(list 'foo n))
Obviously
(defun foo-it-1 (n)
(foo n))
Won't work. It would call the function FOO
on n
instead of constructing a list.
(defun foo-it-2 (n)
'(foo n))
Above would also not work, since the list (foo n)
is not evaluated and returned as is.
Now Lisp has a backquote syntax:
(defun foo-it-3 (n)
`(foo ,n))
Above will work. The comma marks that the symbol n
will be evaluated.
For your problem. Replace foo
with quote
:
(list 'foo n) -> (list 'quote n)
or
`(foo ,n) -> `(quote ,n)
The main difference is that the Lisp printer will always print (foo 3)
as (foo 3)
. FOO
has no special purpose. But QUOTE
has (because it is defined in the language) and the Lisp printer may print (quote 3)
as '3
.
Example:
CL-USER 14 > (progn
(write (list 'foo 3) :pretty nil)
(terpri)
(write (list 'foo 3) :pretty t)
(values))
(FOO 3)
(FOO 3)
CL-USER 15 > (progn
(write (list 'quote 3) :pretty nil)
(terpri)
(write (list 'quote 3) :pretty t)
(values))
(QUOTE 3)
'3
That's just because in Lisp, the quote
character is defined built-in syntax and the printer knows about it.
Also note that printing is also modified by a set of special symbols like *print-pretty*
, *print-readably*
, .... So different Lisp setups might print different, based on the settings on those variables.
Lisp has lists, like: (mary threw the ball).
Some lists describe a computation, like: (* pi (+ r r))
That first list is data and the second is an expression suitable for the evaluator to chew on.
But we need a way to include data in expressions. So decades ago a special operator was introduced to enable that, like: (count-nouns (quote (mary threw the ball)))
Time passed and developers to tired of having to type (quote ...), so they invented a short hand notation: (count-nouns '(mary threw the ball)).
These days the shorthand is implemented at read time, as the characters are read out of the file or off the terminal. This is done with a facility know as "reader macros." This name confuses most beginners, since there is another facility in the language called "macros" which really has little if anything to do with reading the files. They are typically used to add a little syntactic sugar. As in this quote example. After the file (or terminal) stream has been read everything is just a list.
You will notice that if you enter: (quote bob) into your lisp prompt you will get back: 'bob. The printer is helpfully aware of the convention that (quote bob) has a shorthand form. If you ever decide to write your own reader macros you may want to teach the printer how to play along.
Macros, as v.s. reader macros, let you introduce your own special operators. And that lets you embed custom domain specific languages into your code. So - sort of - macros let you extend the semantics and reader macros let you extend the syntax. Macros effect the behavior of the compiler and the evaluator.
You're being confused by the reader macro '.
Lisp's top level is read / eval / print
if you read '(foo bar) you get (quote (foo bar))
if you eval (quote (foo bar)) you get (foo bar)