One of a series of tutorials about Scheme in general
and the Wraith Scheme interpreter in particular.
Copyright © 2011
Jay Reynolds Freeman,
all rights reserved.
Personal Web Site: http://JayReynoldsFreeman.com
Scheme provides a collection of mechanisms for dealing with Scheme objects without evaluating them, which collectively are called quoting. They include several procedures, notably "quote", as well as a few special characters, like ' -- the apostrophe or single-quote -- which may be used as shorthand when quoting, to make code easier to read and write. The essential idea is very simple, however; it is that quoting allows Scheme to deal with objects without evaluating them.
I introduced quoting in the tutorial about lists, as a way to save typing and improve clarity, using the single-quote character. Let's review. Recall that when you type a Scheme expression preceded by a single quote, with no white space in between, Scheme returns the expression unevaluated -- with no further processing. Here are some of the examples from the "Lists" tutorial:
'(1 2 3) ;; ==> (1 2 3) '() ;; ==> () '(1 (2 (3 (4)) (5) ()) (6 (7 8))) ;; ==> (1 (2 (3 (4)) (5) ()) (6 (7 8)))
You can quote any Scheme object or expression, not just lists.
'23 ;; ==> 23 '"meow" ;; ==> "meow" 'define ;; ==> define 'set! ;; ==> set! 'xglerb ;; ==> xglerb 'sqrt ;; ==> sqrt '+ ;; ==> +
Quoting provides many straightforward illustrations of how Scheme can treat programs as data. The next example shows that when Scheme reads a quoted instance of a "sqrt" procedure application, it returns a list -- a data object -- that becomes a procedure application when you evaluate it. (In this example, "e::cons-with-continuation" is not a standard Scheme procedure, it is an enhancement -- "e" for enhancement -- that I added to Wraith Scheme because I found it useful.)
(define my-procedure 'sqrt) ;; ==> my-procedure (define my-expression (list my-procedure -1)) ;; ==> my-expression my-expression ;; ==> (sqrt -1) (e::cons-with-continuation my-expression) ;; ==> +i
Now let's learn something new.
You might wonder what kind of Scheme object a single-quote is. If you try typing just a single-quote into Wraith Scheme (with a "return" character afterward, of course), you will get
' Problem: Unexpected white space, after "'". (Resetting) Top-level loop ...
That is kind of a clue. What is going on is that Wraith Scheme never really processed the single-quote at all. Instead, Wraith Scheme took the single-quote as a cue that something else -- a Scheme expression of some kind -- was supposed to follow, and complained when nothing showed up. (Recall that there is supposed to be no white space between the single-quote and the following expression. The "return" character that you pressed counts as white space, and that was the basis for Wraith Scheme's complaint.)
What actually happens is that the part of the Wraith Scheme program that reads what you type in the Input Panel does some preprocessing of the typed input. In this case, it converts expressions like
into uses of a special form whose name is "quote". That is, by the time Wraith Scheme sees your typed
your typed expression has been converted into
which is a perfectly ordinary use of a special form.
When Wraith Scheme encounters a procedure application, it automatically evaluates all of the procedure's arguments before passing them to the procedure itself. In contrast, when Wraith Scheme encounters an expression like
in which the first item after the left parenthesis is the name of a special form, it lets the special form operation itself decide whether or not to evaluate its argument or arguments. The "quote" special form is very simple -- it takes just one argument, it does not evaluate it, and it returns that unevaluated argument as its own result.
You can see that "quote" names a special form by typing it into Wraith Scheme.
quote ;; ==> #<Built-in expression "quote">
The term "built-in expression" is used there because strictly, the R5 report does not use the term "special form"; rather, it describes things like "quote" rather obliquely, without explicitly giving them a general name. Notwithstanding, the term "special form" is widely used in the Lisp community in this context, and I need a name for those things, so I will use that one.
Be sure you understand that
produces exactly the same result as
Try it and see:
(quote whatever) ;; ==> whatever 'whatever ;; ==> whatever '(1 2 3) ;; ==> (1 2 3) (quote (1 2 3)) ;; ==> (1 2 3)
The reasons for having the single-quote character work this way are of course to save typing and to make things easier to read. Incidentally, cases of programming language software using preprocessing to change one form of input into another before the "real" software sees it are so common that they have a special name: The un-preprocessed form, which is supposedly easier to read, is said to be syntactic sugar; the idea is that sugar makes things more palatable, and what the preprocessing does is change the syntax of the language to make it easier to swallow -- easier for you to use. Thus we might say that
is syntactic sugar for
Scheme uses other kinds of syntactic sugar; we will see more of them later in this tutorial, and still more in subsequent tutorials.
Quoting is all well and good, but sometimes it is too all-encompassing: It turns out that programmers often want to write a long Scheme expression in which almost everything is quoted, but some things are not. For example, you might want to create an expression like
(define my-expression '(1 2 3 x 4 5)) ;; ==> my-expression
and somehow, magically arrange that x was not quoted, so that if there were a pre-existing value for x, it that would be incorporated into "my-expression". That is, you would like to do something like this:
(define x 42) ;; ==> x (define my-expression '(1 2 3 x 4 5)) ;; ==> my-expression
and then have
my-expression ;; ==> (1 2 3 42 4 5) (That doesn't happen!)
Of course, that is not how "quote" works; if you do try to evaluate "my-expression", you will get
my-expression ;; ==> (1 2 3 x 4 5)
with "x" still an unevaluated expression in the middle of the list.
It turns out that there are more special forms to do just what you want. Someone decided that if you could quote things, you ought to be able to unquote them as well: It ought to be possible to have a big, long quoted expression and have a special form that you could wrap around pieces of it, that would result in them not being quoted.
The way you do that is by using a different special form, "quasiquote", instead of "quote". (I believe that the reason for using a different special form rather than just modifying and enhancing "quote" had to do with efficiency -- "quote" is relatively simple, but "quasiquote" is complicated, and takes more time to execute. Thus it is more efficient to be able to use just "quote" when that is all you need.)
What you do is "quasiquote" your expression rather than "quote" it. Then, inside the expression, you use yet another special form, "unquote", to indicate what parts you do not want quoted. So the solution to the example given above is this:
(define x 42) ;; ==> x (define my-expression (quasiquote (1 2 3 (unquote x) 4 5))) ;; ==> my-expression my-expression ;; ==> (1 2 3 42 4 5)
There is single-character syntactic sugar for both "quasiquote" and "unquote", just as for "quote". The characters to use are respectively `, sometimes called backquote, and , -- which is just plain "comma". Thus
is syntactic sugar for
is syntactic sugar for
So the previous example could be written more simply, as
(define x 42) ;; ==> x (define my-expression `(1 2 3 ,x 4 5)) ;; ==> my-expression my-expression ;; ==> (1 2 3 42 4 5)
Note in passing that it is an error to try to get Scheme to evaluate an unquoted expression without an enclosing quasiquote. If you enter
you will get an error message:
,whatever (unquote whatever) Problem: Encountered "unquote" when not quoting. (Resetting) Top-level loop ...
Here are some more examples of unquoting:
(define a 73) ;; ==> a (define b 88) ;; ==> b `(1 2 ,a ,b 3) ;; ==> (1 2 73 88 3) `(1 2 (,a ,b) 3) ;; ==> (1 2 (73 88) 3) `(1 2 ,(list a b) 3) ;; ==> (1 2 (73 88) 3)
The last example is a little tricky. The unquoting made the entire expression "(list a b)" get evaluated, so that the result of that evaluation was spliced into the result of the overall quasiquoted expression. There are other very similar quasiquoted expressions, and you have to watch carefully to make sure you are using the one you want. For example, you might alternatively type
`(1 2 (list ,a ,b) 3) ;; ==> (1 2 (list 73 88) 3)
in which the text "list" was not unquoted, so that it appears unevaluated in the result. The point is, that these special forms are very powerful, and give you lots of way to use them; you have to make sure you are making the right choice. (Remember -- the price of freedom is often the freedom to make mistakes.)
There is one more special form that has to do with unquoting. Its name is "unquote-splicing", and it has two characters worth of syntactic sugar, namely ",@". Its purpose is to unquote a list -- the expression that is unquote-spliced must evaluate to a list -- and then to insert the individual items of the list into the result of the quasiquoted expression.
`(1 2 ,@(list a b) 3) ;; ==> (1 2 73 88 3)
What is going on here is as if the "," of the ",@" turns off quoting, so that "(list a b)" gets evaluated, with result (73 88), and then the "@" tells Scheme to splice the elements of that list, one at a time and in order, into the result as a whole.
Note the difference between "unquote" and "unquote-splicing" carefully:
`(1 2 ,(list a b) 3) ;; ==> (1 2 (73 88) 3) `(1 2 ,@(list a b) 3) ;; ==> (1 2 73 88 3)
Without the "@", you get the entire unquoted list spliced in as a whole; with "@", you get its individual elements, one at a time.
And of course, because of the preprocessing of the syntactic sugar,
`(1 2 ,@(list a b) 3)
is precisely equivalent to
(quasiquote (1 2 (unquote-splicing (list a b)) 3))
You might alternatively have gotten the result of this example as follows:
(define my-list (list a b)) ;; ==> my-list my-list ;; ==> (73 88) `(1 2 ,@my-list 3) ;; ==> (1 2 73 88 3)
The "unquote-splicing" operation works fine, as long as the expression that is unquote-spliced evaluates to a list.
There is still more power available, because "quasiquote", "unquote", and "unquote-splicing" nest. If several "quasiquotes" affect part of an expression, there must be the same number of "unquote"s or "unquote-splicings", or any combination of both, before evaluation gets turned on again. For example:
``(1 2 ,(list a ,b) 3) ;; ==> (quasiquote (1 2 (unquote (list a 88)) 3))
Only the subexpression "b" of the input expression was evaluated, because only it had two ","s in front of it, to match the two `s at the start of the whole expression. What good is that? Well, its real uses are in moderately advanced Scheme programming, so the example I am about to give is a bit complicated. Suppose you did this:
(define my-expression ``(1 2 ,(list a ,b) 3)) ;; ==> my-expression my-expression ;; ==> (quasiquote (1 2 (unquote (list a 88)) 3)) (e::cons-with-continuation my-expression) ;; ==> (1 2 (73 88) 3)
If you have been typing these examples into Wraith Scheme, the current value of "a" is 73 -- that is how the "73" ended up in the last result. Let's change the value of "a" and try again:
(set! a "Hello, world!") ;; ==> a (e::cons-with-continuation my-expression) ;; ==> (1 2 ("Hello, world!" 88) 3)
What we did in creating "my-expression" was to create a procedural application that is all set to pick up whatever value of "a" is current when the procedural application gets evaluated, and use that value.
There is one last thing I should mention about quoting. Scheme defines quoting as a means of creating constant objects. They are not supposed to change. So if you try to change an object that you created with "quote" or "quasiquote", Scheme won't let you: You cannot modify a quoted or quasiquoted object. I can't give any examples of this property yet, because we have not yet encountered any of the procedures for changing objects. We will see some in a later tutorial.
If you are confused by the fancier aspects of quoting, don't worry too much: Most uses of quoting in Scheme are simple instances of putting a single-quote in front of something, and doing so saves lots of typing and makes code much easier to read, so it is a big win. Yet the more complicated features are there when you need them, and if you get seriously into Scheme, you will eventually learn how to use them.
-- Jay Reynolds Freeman (Jay_Reynolds_Freeman@mac.com)