================================================================== Hunk N starts here:
==================================================================
3.5.1 Replacing Procedure Values
Earlier we showed how to replace normal data values in variable bindings, using the side-eecting special formset!.
We can also change procedure values. One way of doing this is just to change the value of the procedure variable. (Remember that a named procedure is really just a procedure object that happens to have a pointer to it stored in that variable.)
Just as we changed the value of the variable myvar using set!, we can change the value of the
procedure variable quadruple. Try this: Scheme>(quadruple 3)
12
Scheme>(set! quadruple double) #'procedure]
Scheme>(quadruple 3) 6
What happened here is that when we evaluated the expression (set! quadruple double) it
just did the usual thing set! does when both of its arguments are variables|it computed the
value of the expression on the right, in this case by fetching the value from the binding ofdouble,
(a pointer) to a procedure|the one that we created when wedefine'ddouble. This pointer was
copied into quadruple, so that it now contains a pointer to the very same procedure.
Calling quadruplenow has the same eect as calling double, because either way, a pointer is
fetched from the variable, and whatever it points to is called.
Note that while this illustrates how Scheme works, and we'll show why it's handy later, it's not usually a great idea to go around changing the values of procedure variables by side-eecting them withset!.
Usually, once a program has been developed, you don't want to clobber named procedures, because it makes the code hard to understand|you don't want your nished program to go around changing the meaning of procedure names as it runs. (You normally want to be able to look at your program and see the denitions, and not have to worry that some other part of the program may change the procedures at odd moments.)
During interactive development of a program, however, it's often very convenient to be able to change a procedure's behavior at will. (We're not really modifying a procedure, though|we're changing a variable binding's value to aect which procedure is called. We don't have to actually modify any procedure objects, because we can replace a pointer to one procedure with a pointer to another.)
Usually you'll want to do this by redening the procedure with another defineexpression.
For example, suppose we want to restore the old behavior of quadruple, which we foolishly
clobbered above. We can simply defineit again, the old way: Scheme>(define (quadruple x) (double (double x))) quadruple
In a nished program, you generally shouldn't have multiple denitions of the same thing|a
defineform should dene something that doesn't change during program execution. If you want to
change the state of a binding, useset!to make it clear that's what's going on, and put a comment
at the denition of the variable warning that it is likely to be changed at runtime.
Most interactive Scheme systems let youdefine the same variables multiple times, though, so
that you can change things during program development. (Note that we're talking about redening the same program variable here, not dening dierent variables with the same name in dierent scopes.)
3.5.2 Loading Code from a File
When you're actually developing a program, you often want to save the text in a le, rather than just typing it in and losing it when you exit the Scheme system.
The simplest way of doing this is to use an editor in one window and Scheme in another. From the editor, save your program text into a le, and then load it into Scheme with theloadprocedure. loadtakes a string as an argument, which is the name of the le to load, and reads it in just as
though you had typed it in by hand, at the prompt. (A string literal is written with double quotes around it there'll be more about strings more later.)
Type the following text into your editor and save it into a le named triple.scm. (define (triple x)
(+ x (+ x x)))
Now, at the Scheme prompt, load the le and call the procedure:
Scheme>(load "triple.scm") loading...triple...done Scheme>(triple 3)
9
(Notice that in the above example, there's no connection between the string we used to name the le, "triple.scm", and the name of the procedure, triple. We just chose to call the le "triple.scm"to remind us what's in it.)
Usually, when you're developing a program, you should put only a few denitions in a le| maybe just one. This lets you change small parts of your program, saved the changed le, and reload the le to change the denitions in your running Scheme system.
Good editors also have packages that allow you to run Scheme and use an editor command to send the contents of a le (or a selected region of a le) to Scheme, as though you'd typed it in. (Emacs has excellent facilities for this.)
If you're using a graphical user interface, you may be able to simply cut text from your editor, and paste it into the window you have Scheme running in, so that it appears to Scheme as though you'd just typed it in.
Be careful about reloading denitions. When you load a le, the Scheme system will reuse the same top-level bindings, and reinitialize them. In general, new objects will be constructed, even if the textual denitions haven't changed.
For example, suppose we have the following code in a le, which we've already loaded once:
(define my-list (list 1 2))
(define my-other-list (cdr my-list))
If we reload this le, all three denitions will be processed again. A new list will be constructed and the existing binding ofmy-list will be updated to point at the new list.
Likewise, the existing binding of my-other-list will be updated with the cdr of that new
list. Each time we reload the le, we'll recreate the intended data structure, including the sharing relationship between the two lists.
But now consider what happens if this code is spread across two les, with the denition of
my-other-listin a dierent le, which we don't reload. If we just reload the rst denition, then
the binding my-other-listwill still refer to thecdrof the old list, not the new one. If your code
depends on the two lists sharing structure, it not behave as expected, because the two variables' bindings will refer to distinct lists.
Procedures can cause the same sorts of problems. If you have a pointer to a procedure in a data structure, and then you redene the procedure by modifying the denition and reloading it, a new procedure object will be created, but the old data structure will still hold a pointer to the old procedure object.
In general, you should be careful to recreate any data structures holding procedures if you rede- ne those procedures. This is usually easy, if you reload the code that creates the data structures, after reloading the new denitions of the procedures.
Notice that this is not necessary if you just call top-level procedures (or look up variable values) in the usual way. For example, given our earlier denitions of double and quadruple, changing doubleaectsquadrupleimmediately. Every time we callquadruple, it fetches the current value
of the binding of double, which ensures that it sees the most recent version. We can reload the
3.5.3 Loading and Running Whole Programs
to be written ]