I know that you can use '
(aka quote
) to create a list, and I use this all the time, like this:
> (car '(1 2 3))
1
But it doesn’t always work like I’d expect. For example, I tried to create a list of functions, like this, but it didn’t work:
> (define math-fns '(+ - * /))
> (map (lambda (fn) (fn 1)) math-fns)
application: not a procedure;
expected a procedure that can be applied to arguments
given: '+
When I use list
, it works:
> (define math-fns (list + - * /))
> (map (lambda (fn) (fn 1)) math-fns)
'(1 -1 1 1)
Why? I thought '
was just a convenient shorthand, so why is the behavior different?
TL;DR: They are different; use
list
when in doubt.A rule of thumb: use
list
whenever you want the arguments to be evaluated;quote
“distributes” over its arguments, so'(+ 1 2)
is like(list '+ '1 '2)
. You’ll end up with a symbol in your list, not a function.An in-depth look at
list
andquote
In Scheme and Racket,
quote
andlist
are entirely different things, but since both of them can be used to produce lists, confusion is common and understandable. There is an incredibly important difference between them:list
is a plain old function, whilequote
(even without the special'
syntax) is a special form. That is,list
can be implemented in plain Scheme, butquote
cannot be.The
list
functionThe
list
function is actually by far the simpler of the two, so let’s start there. It is a function that takes any number of arguments, and it collects the arguments into a list.This above example can be confusing because the result is printed as a
quote
able s-expression, and it’s true, in this case, the two syntaxes are equivalent. But if we get slightly more complicated, you’ll see that it is different:What’s going on in the
quote
example? Well, we’ll discuss that in a moment, but first, take a look atlist
. It’s just an ordinary function, so it follows standard Scheme evaluation semantics: it evaluates each of its arguments before they get passed to the function. This means that expressions like(+ 1 1)
will be reduced to2
before they get collected into the list.This behavior is also visible when supplying variables to the list function:
With
list
, thex
gets evaluated before getting passed tolist
. Withquote
, things are more complicated.Finally, because
list
is just a function, it can be used just like any other function, including in higher-order ways. For example, it can be passed to themap
function, and it will work appropriately:The
quote
formQuotation, unlike
list
, is a special part of Lisps. Thequote
form is special in part because it gets a special reader abbreviation,'
, but it’s also special even without that. Unlikelist
,quote
is not a function, and therefore it does not need to behave like one—it has rules of its own.A brief discussion of Lisp source code
In Lisp, of which Scheme and Racket are derivatives, all code is actually made up of ordinary data structures. For example, consider the following expression:
That expression is actually a list, and it has three elements:
+
symbol1
2
All of these values are normal values that can be created by the programmer. It’s really easy to create the
1
value because it evaluates to itself: you just type1
. But symbols and lists are harder: by default, a symbol in the source code does a variable lookup! That is, symbols are not self-evaluating:As it turns out, though, symbols are basically just strings, and in fact we can convert between them:
Lists do even more than symbols, because by default, a list in the source code calls a function! Doing
(+ 1 2)
looks at the first element in the list, the+
symbol, looks up the function associated with it, and invokes it with the rest of the elements in the list.Sometimes, though, you might want to disable this “special” behavior. You might want to just get the list or get the symbol without it being evaluated. To do this, you can use
quote
.The meaning of quotation
With all this in mind, it’s pretty obvious what
quote
does: it just “turns off” the special evaluation behavior for the expression that it wraps. For example, considerquote
ing a symbol:Similarly, consider
quote
ing a list:No matter what you give
quote
, it will always, always spit it back out at you. No more, no less. That means if you give it a list, none of the subexpressions will be evaluated—do not expect them to be! If you need evaluation of any kind, uselist
.Now, one might ask: what happens if you
quote
something other than a symbol or a list? Well, the answer is... nothing! You just get it back.This makes sense, since
quote
still just spits out exactly what you give it. This is why “literals” like numbers and strings are sometimes called “self-quoting” in Lisp parlance.One more thing: what happens if you
quote
an expression containingquote
? That is, what if you “doublequote
”?What happened there? Well, remember that
'
is actually just a direct abbreviation forquote
, so nothing special happened at all! In fact, if your Scheme has a way to disable the abbreviations when printing, it will look like this:Don’t be fooled by
quote
being special: just like(quote (+ 1))
, the result here is just a plain old list. In fact, we can get the first element out of the list: can you guess what it will be?If you guessed
3
, you are wrong. Remember,quote
disables all evaluation, and an expression containing aquote
symbol is still just a plain list. Play with this in the REPL until you are comfortable with it.Quotation is incredibly simple, but it can come off as very complex because of how it tends to defy our understanding of the traditional evaluation model. In fact, it is confusing because of how simple it is: there are no special cases, there are no rules. It just returns exactly what you give it, precisely as stated (hence the name “quotation”).
Appendix A: Quasiquotation
So if quotation completely disables evaluation, what is it good for? Well, aside from making lists of strings, symbols, or numbers that are all known ahead of time, not much. Fortunately, the concept of quasiquotation provides a way to break out of the quotation and go back into ordinary evaluation.
The basics are super simple: instead of using
quote
, usequasiquote
. Normally, this works exactly likequote
in every way:What makes
quasiquote
special is that is recognizes a special symbol,unquote
. Whereverunquote
appears in the list, then it is replaced by the arbitrary expression it contains:This lets you use
quasiquote
to construct templates of sorts that have “holes” to be filled in withunquote
. This means it’s possible to actually include the values of variables inside of quoted lists:Of course, using
quasiquote
andunquote
is rather verbose, so they have abbreviations of their own, just like'
. Specifically,quasiquote
is`
(backtick) andunquote
is,
(comma). With those abbreviations, the above example is much more palatable.One final point: quasiquote actually can be implemented in Racket using a rather hairy macro, and it is. It expands to usages of
list
,cons
, and of course,quote
.Appendix B: Implementing
list
andquote
in SchemeImplementing
list
is super simple because of how “rest argument” syntax works. This is all you need:That’s it!
In contrast,
quote
is a lot harder—in fact, it’s impossible! It would seem totally feasible, since the idea of disabling evaluation sounds a lot like macros. Yet a naïve attempt reveals the trouble:We just take
arg
and spit it back out... but this doesn’t work. Why not? Well, the result of our macro will be evaluated, so all is for naught. We might be able to expand to something sort of likequote
by expanding to(list ...)
and recursively quoting the elements, like this:Unfortunately, though, without procedural macros, we can’t handle symbols without
quote
. We could get closer usingsyntax-case
, but even then, we would only be emulatingquote
’s behavior, not replicating it.Appendix C: Racket printing conventions
When trying the examples in this answer in Racket, you may find that they do not print as one would expect. Often, they may print with a leading
'
, such as in this example:This is because Racket, by default, prints results as expressions when possible. That is, you should be able to type the result into the REPL and get the same value back. I personally find this behavior nice, but it can be confusing when trying to understand quotation, so if you want to turn it off, call
(print-as-expression #f)
, or change the printing style to “write” in the DrRacket language menu.