After making it through the major parts of an introductory Lisp book, I still couldn't understand what the special operator (quote)
(or equivalent '
) function does, yet this has been all over Lisp code that I've seen.
What does it do?
lispquote
After making it through the major parts of an introductory Lisp book, I still couldn't understand what the special operator (quote)
(or equivalent '
) function does, yet this has been all over Lisp code that I've seen.
What does it do?
To give the short answer, macros are used for defining language syntax extensions to Common Lisp or Domain Specific Languages (DSLs). These languages are embedded right into the existing Lisp code. Now, the DSLs can have syntax similar to Lisp (like Peter Norvig's Prolog Interpreter for Common Lisp) or completely different (e.g. Infix Notation Math for Clojure).
Here is a more concrete example:
Python has list comprehensions built into the language. This gives a simple syntax for a common case. The line
divisibleByTwo = [x for x in range(10) if x % 2 == 0]
yields a list containing all even numbers between 0 and 9. Back in the Python 1.5 days there was no such syntax; you'd use something more like this:
divisibleByTwo = []
for x in range( 10 ):
if x % 2 == 0:
divisibleByTwo.append( x )
These are both functionally equivalent. Let's invoke our suspension of disbelief and pretend Lisp has a very limited loop macro that just does iteration and no easy way to do the equivalent of list comprehensions.
In Lisp you could write the following. I should note this contrived example is picked to be identical to the Python code not a good example of Lisp code.
;; the following two functions just make equivalent of Python's range function
;; you can safely ignore them unless you are running this code
(defun range-helper (x)
(if (= x 0)
(list x)
(cons x (range-helper (- x 1)))))
(defun range (x)
(reverse (range-helper (- x 1))))
;; equivalent to the python example:
;; define a variable
(defvar divisibleByTwo nil)
;; loop from 0 upto and including 9
(loop for x in (range 10)
;; test for divisibility by two
if (= (mod x 2) 0)
;; append to the list
do (setq divisibleByTwo (append divisibleByTwo (list x))))
Before I go further, I should better explain what a macro is. It is a transformation performed on code by code. That is, a piece of code, read by the interpreter (or compiler), which takes in code as an argument, manipulates and the returns the result, which is then run in-place.
Of course that's a lot of typing and programmers are lazy. So we could define DSL for doing list comprehensions. In fact, we're using one macro already (the loop macro).
Lisp defines a couple of special syntax forms. The quote ('
) indicates the next token is a literal. The quasiquote or backtick (`
) indicates the next token is a literal with escapes. Escapes are indicated by the comma operator. The literal '(1 2 3)
is the equivalent of Python's [1, 2, 3]
. You can assign it to another variable or use it in place. You can think of `(1 2 ,x)
as the equivalent of Python's [1, 2, x]
where x
is a variable previously defined. This list notation is part of the magic that goes into macros. The second part is the Lisp reader which intelligently substitutes macros for code but that is best illustrated below:
So we can define a macro called lcomp
(short for list comprehension). Its syntax will be exactly like the python that we used in the example [x for x in range(10) if x % 2 == 0]
- (lcomp x for x in (range 10) if (= (% x 2) 0))
(defmacro lcomp (expression for var in list conditional conditional-test)
;; create a unique variable name for the result
(let ((result (gensym)))
;; the arguments are really code so we can substitute them
;; store nil in the unique variable name generated above
`(let ((,result nil))
;; var is a variable name
;; list is the list literal we are suppose to iterate over
(loop for ,var in ,list
;; conditional is if or unless
;; conditional-test is (= (mod x 2) 0) in our examples
,conditional ,conditional-test
;; and this is the action from the earlier lisp example
;; result = result + [x] in python
do (setq ,result (append ,result (list ,expression))))
;; return the result
,result)))
Now we can execute at the command line:
CL-USER> (lcomp x for x in (range 10) if (= (mod x 2) 0))
(0 2 4 6 8)
Pretty neat, huh? Now it doesn't stop there. You have a mechanism, or a paintbrush, if you like. You can have any syntax you could possibly want. Like Python or C#'s with
syntax. Or .NET's LINQ syntax. In end, this is what attracts people to Lisp - ultimate flexibility.
What's going on?
You say: feel like there is nothing special is going on here. The outer foo
in bar
increments the global x
, and foo
surrounded by let
in bar
increments the shadowed x
. What's the big deal?
The special that's going on here is that LET
can shadow the value of *x*
. With lexical variables that's not possible.
The code declares *x*
to be special via the DEFVAR
.
In FOO
now the value of *x*
is looked up dynamic. FOO
will take the current dynamic binding of *x*
or, if there is none, the symbol value of the symbol *x*
. A new dynamic binding can, for example, be introduced with LET
.
A lexical variable on the other hand has to be present in the lexical environment somewhere. LET
, LAMBDA
, DEFUN
and others can introduce such lexical variables. See here the lexical variable x
introduced in three different ways:
(let ((x 3))
(* (sin x) (cos x)))
(lambda (x)
(* (sin x) (cos x)))
(defun baz (x)
(* (sin x) (cos x)))
If our code were:
(defvar x 0)
(let ((x 3))
(* (sin x) (cos x)))
(lambda (x)
(* (sin x) (cos x)))
(defun baz (x)
(* (sin x) (cos x)))
Then X
were special in all three above cases, because of the DEFVAR
declaration, which declares X
to be special - globally for all levels. Because of this, there is the convention to declare special variables as *X*
. Thus only variables with stars around them are special - by convention. That's a useful convention.
In your code you have then:
(defun bar ()
(foo)
(let ((*x* 20))
(foo))
(foo))
Since *x*
has be declared special via the DEFVAR
above in your code, the LET
construct introduces a new dynamic binding for *x*
. FOO
is then called. Since inside FOO
the *x*
uses dynamic binding, it looks up the current one and finds that *x*
is dynamically bound to 20
.
The value of a special variable is found in the current dynamic binding.
Local SPECIAL declarations
There are also local special
declarations:
(defun foo-s ()
(declare (special *x*))
(+ *x* 1))
If the variable had been declared special by a DEFVAR
or DEFPARAMETER
, then the local special
declaration can be omitted.
A lexical variable directly references the variable binding:
(defun foo-l (x)
(+ x 1))
Let's see it in practice:
(let ((f (let ((x 10))
(lambda ()
(setq x (+ x 1))))))
(print (funcall f)) ; form 1
(let ((x 20)) ; form 2
(print (funcall f))))
Here all variables are lexical. In form 2 the LET
will not shadow the X
in our function f
. It can't. The function uses the lexical bound variable, introduced by the LET ((X 10)
. Surrounding the call with another lexically bound X
in form 2 has no effect on our function.
Let's try special variables:
(let ((f (let ((x 10))
(declare (special x))
(lambda ()
(setq x (+ x 1))))))
(print (funcall f)) ; form 1
(let ((x 20)) ; form 2
(declare (special x))
(print (funcall f))))
What now? Does that work?
It does not!
The first form calls the function and it tries to look up the dynamic value of X
and there is none. We get an error in form 1: X
is unbound, because there is no dynamic binding in effect.
Form 2 would work, since the LET
with the special
declaration introduces a dynamic binding for X
.
Best Answer
Short answer Bypass the default evaluation rules and do not evaluate the expression (symbol or s-exp), passing it along to the function exactly as typed.
Long Answer: The Default Evaluation Rule
When a regular (I'll come to that later) function is invoked, all arguments passed to it are evaluated. This means you can write this:
Which in turn evaluates
(+ a 2)
, by evaluatinga
and 2. The value of the symbola
is looked up in the current variable binding set, and then replaced. Saya
is currently bound to the value 3:We'd get
(+ 3 2)
, + is then invoked on 3 and 2 yielding 5. Our original form is now(* 5 3)
yielding 15.Explain
quote
Already!Alright. As seen above, all arguments to a function are evaluated, so if you would like to pass the symbol
a
and not its value, you don't want to evaluate it. Lisp symbols can double both as their values, and markers where you in other languages would have used strings, such as keys to hash tables.This is where
quote
comes in. Say you want to plot resource allocations from a Python application, but rather do the plotting in Lisp. Have your Python app do something like this:Giving you output looking like this (slightly prettyfied):
Remember what I said about
quote
("tick") causing the default rule not to apply? Good. What would otherwise happen is that the values ofallocate
andfree
are looked up, and we don't want that. In our Lisp, we wish to do:For the data given above, the following sequence of function calls would have been made:
But What About
list
?Well, sometimes you do want to evaluate the arguments. Say you have a nifty function manipulating a number and a string and returning a list of the resulting ... things. Let's make a false start:
Hey! That's not what we wanted. We want to selectively evaluate some arguments, and leave the others as symbols. Try #2!
Not Just
quote
, Butbackquote
Much better! Incidently, this pattern is so common in (mostly) macros, that there is special syntax for doing just that. The backquote:
It's like using
quote
, but with the option to explicitly evaluate some arguments by prefixing them with comma. The result is equivalent to usinglist
, but if you're generating code from a macro you often only want to evaluate small parts of the code returned, so the backquote is more suited. For shorter lists,list
can be more readable.Hey, You Forgot About
quote
!So, where does this leave us? Oh right, what does
quote
actually do? It simply returns its argument(s) unevaluated! Remember what I said in the beginning about regular functions? Turns out that some operators/functions need to not evaluate their arguments. Such as IF -- you wouldn't want the else branch to be evaluated if it wasn't taken, right? So-called special operators, together with macros, work like that. Special operators are also the "axiom" of the language -- minimal set of rules -- upon which you can implement the rest of Lisp by combining them together in different ways.Back to
quote
, though:Compare to (on Steel-Bank Common Lisp):
Because there is no
spiffy-symbol
in the current scope!Summing Up
quote
,backquote
(with comma), andlist
are some of the tools you use to create lists, that are not only lists of values, but as you seen can be used as lightweight (no need to define astruct
) data structures!If you wish to learn more, I recommend Peter Seibel's book Practical Common Lisp for a practical approach to learning Lisp, if you're already into programming at large. Eventually on your Lisp journey, you'll start using packages too. Ron Garret's The Idiot's Guide to Common Lisp Packages will give you good explanation of those.
Happy hacking!