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?
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)
Example: If you want to construct a list with a symbol
foo
and a variable number:(foo <some-number>)
Now we write a function for it:
Obviously
Won't work. It would call the function
FOO
onn
instead of constructing a list.Above would also not work, since the list
(foo n)
is not evaluated and returned as is.Now Lisp has a backquote syntax:
Above will work. The comma marks that the symbol
n
will be evaluated.For your problem. Replace
foo
withquote
:or
The main difference is that the Lisp printer will always print
(foo 3)
as(foo 3)
.FOO
has no special purpose. ButQUOTE
has (because it is defined in the language) and the Lisp printer may print(quote 3)
as'3
.Example:
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.
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
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 objectsomething
without evaluating it. ThusNow, 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 formsin 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
instead. The compiler ends up getting exactly the same input; a list whose first element is the symbol
quote
and whose second value is2
or(a b c)
.Now we can talk about your code. The form
returns a list whose first element is the symbol
quote
, and whose second element is the value of the variablex
. That can be printed asNow, 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
Now we've got enough to address your question about the book's code:
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 symbolquote
and whose second element is the symboleast
. 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 symbolquote
and whose second element is the symboleast
. 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 dobut 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 symbolquote
and whose second element is<value-of-x>
. When you usequote-it
with other values, you'll see the problem:You still get a symbol back, because
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 symbolquote
andsomething
, the value is the literalsomething
. Since the body ofquote-it
is a list of the symbolquote
and the symbolx
, the value of the body ofquote-it
is the symbolx
.