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
All the programming languages I can think of have a way to name things -- to associate a name with some data item you are using -- and Scheme is no exception. In this tutorial we will learn how to do so.
First, let's review. Suppose we are using a variable named "x", and we want its value to be "2". Here are the statements required to do that in several other programming languages:
x = 2; In C, C++, Objective C, or Java. x = 2 In Ruby. x := 2; In Pascal. Does anybody remember Pascal? let x = 2 In several kinds of Basic.
In all the examples, there is some kind of infix operator to do the job; that is, there is something like "=" or ":=" that goes in between the name of the variable and its intended value. You have probably seen enough Scheme by now to suspect that Scheme doesn't use infix operators, and indeed, it does not. What is more, Scheme has two ways to give a value to a variable:
(define x 2) (set! x 2) ("set!" is usually pronounced "set-bang")
Now, why in the world is that?
I am going to answer the question twice, once for those of you who are newcomers to any kind of programming, and once for you who are more experienced. I will start with the first group.
When the software that runs a programming language encounters a statement like "x = 2;", it has two rather different kinds of things to deal with. First, it has to make sure that there is some place in the computer's memory to put the value of x: Somewhere, somehow, there must be a little chunk of memory big enough to hold a number, into which "2" will be put. Second, it has to keep track of some kind of filing system or cross-reference, so that it knows to use that particular chunk of memory whenever it encounters the variable name "x".
The reason for having both "define" and "set!" is to separate those two tasks a little bit. We will see why in a moment.
In any case, "define" does three things: It
Suppose there were a Wraith Scheme procedure to feed the cat automatically at a given time. There isn't one, and my cats are constantly complaining about that, but let's pretend. It might work by looking at a variable named "feeding-time", and dishing out the kitty crunchies then. The idea is that "feeding-time" was "define"d long ago, but gets updated by means of "set!" every so often. You would probably be in the habit of regularly typing something like
(set! feeding-time "5:30 AM January 1")
so that the cat would get fed at that time.
One possible problem with this approach to fostering feline nutrition would be making a typographical error. Suppose you misspelled "feeding-time" by mistake, and typed something like
(set! feedign-time "5:30 AM January 1")
If "set!" blindly went ahead, like "define", and created an entry for "feedign-time" in Wraith Scheme's internal look-up table, then the old value of "feeding-time" (spelled correctly) would not get updated, and the cat would not get fed, and that would be bad. Fortunately, if you try to use "set!" with a variable name that is not already known to Wraith Scheme, Wraith Scheme will print an error message. So your typing session might look like this:
(set! feedign-time "5:30 AM January 1") feedign-time Problem: Unbound symbol -- cannot assign a value to it. (Resetting) Top-level loop ...
There are actually plenty of other reasons for making the "define"/"set!" distinction, but they have to do with things like scoping and applicative programming, that are beyond the scope of this tutorial. I hope you see that the distinction can be useful even in simple circumstances, though.
I promised above to say a few words about this distinction for readers who are more experienced programmers, so here goes. The idea is that "define" allocates storage for the given variable, if necessary, and then assigns the given value to it, whereas "set!" merely assigns a value to a previously-allocated variable. Many other programming languages have allocate-and-assign statements, like C/C++/Java's
int x = 2;
Scheme's "define" is similar, but since Scheme is typeless -- any variable may have any kind of value -- there is no need for a keyword like the "int" in the preceding example, to make the parallel more obvious.
Let's give some examples that show how "define" and "set!" work together. In what follows, I will provide one-line Scheme statements that you can type into Wraith Scheme, if you wish, and I will show what Wraith Scheme types back to you, preceded by the text ";; ==>". I am doing that because in Scheme, ";" starts a comment -- Scheme will ignore anything between a semicolon and the end of the line. Thus you can cut and paste whole blocks of these one-liners from this tutorial into Wraith Scheme, and Wraith Scheme will not notice anything after the ";"s.
Remember to press the "return" key after typing or pasting into the Input Panel.
A "define" statement returns the symbol -- the variable name -- that was just defined:
(define x 2) ;; ==>x
Once you have defined a variable, you may type its name (followed by "return") to see its value.
x ;; ==> 2
You may use "set!" to change the value of a variable. Note that "set!" returns "#t", which is a symbol that Scheme uses to represent "true" in logical statements, but which in this context merely means "all done".
(set! x 42) ;; ==> #t x ;; ==> 42
You may redefine a variable if you wish -- it doesn't hurt to use "define" on the same variable more than once.
(define x 137) ;; ==> #t x ;; ==> 137 (define x 1000) ;; ==> #t x ;; ==> 1000
Once you have defined a variable, you can mix "define"s and "set!"s in any combination for as long as you like. It is usually better practice just to use "set!" after the first "define", though.
By the way, variables in Scheme may take on any kind of value. If you are reading these tutorials in order, you haven't seen very many different kinds of Scheme objects yet, but here are some examples that should give you the general idea:
(set! x "Cat") ;; ==> #t -- "Cat" is a string. x ;; ==> "Cat" (set! x #t) ;; ==> #t -- #t is the "true boolean" x ;; ==> #t (set! x #f) ;; ==> #t -- #f is the "false boolean" x ;; ==> #f
As I have pointed out before, it is an error to try to "set!" a variable that is not already known to Scheme.
(set! xglerb 42) ;; ==> produces the following error message: xglerb Problem: Unbound symbol -- cannot assign a value to it. (Resetting) Top-level loop ...
The value given to either "define" or "set!" may be a Scheme expression of any kind, and whatever that expression returns will be the value assigned to the given variable.
(define y (* 2 5)) ;; ==> y y ;; ==> 10 (set! y (string-append "I " "see " "the " "cat!")) ;; ==> #t y ;; ==> "I see the cat!"
If you have been looking closely, you may have noticed that the first argument to "define" and to "set!" -- the variable name -- is not evaluated. I mentioned in an earlier tutorial that when a Scheme procedure application is evaluated, all of the arguments get evaluated first, so what this means is that "define" and "set!" are not procedures; each of them is what the Lisp community calls a macro or a special form. Scheme has a handful of built-in special forms -- we will see more of them later.
There are a few ways to create new variables -- to allocate storage for them -- besides "define", but I will not discuss them in this tutorial. The programmers among you may be wondering how local variables are handled -- this tutorial has all been about what the programming community generally calls global variables. That is what I am avoiding: There is a whole separate tutorial about them.
There is also a special syntax for "define" that is used to create new procedures. There is a separate tutorial for that subject, as well, but as an example, here is the code, spread over two lines, to create a procedure named "increment", that takes one argument, here called "n", and adds one to it.
(define (increment n) (+ n 1)) ;; ==> increment
Having defined it, you can use it like any other procedure:
(increment 9) ;; ==> 10
There are some detailed rules in the "R5" report for what combinations of characters you are allowed to use for variable names. You should probably go and look at them at some time, but if you start out by sticking to names that begin with letters of the alphabet, and that contain only letters, numbers, and "-" and "_", you will do just fine.
-- Jay Reynolds Freeman (Jay_Reynolds_Freeman@mac.com)