add eli's parameterize explanation
svn: r17293
This commit is contained in:
parent
294e593dd2
commit
1e8d979c32
|
@ -5,15 +5,90 @@
|
|||
|
||||
@title[#:tag "parameterize"]{Dynamic Binding: @scheme[parameterize]}
|
||||
|
||||
The @scheme[parameterize] form supports a kind of dynamic binding that
|
||||
is useful for adjusting defaults or passing extra arguments through
|
||||
layers of function calls. The settings that are adjusted by a
|
||||
@scheme[parameterize] form are called @deftech{parameters}.
|
||||
@scheme[parameterize] is used to have values that are "dynamically scoped".
|
||||
You get a parameter with @scheme[make-parameter]. The parameter itself
|
||||
behaves as a function: call it with no inputs and you get its value,
|
||||
call it with one value and it will set the value. The settings that are
|
||||
adjusted by a @scheme[parameterize] form are called @deftech{parameters}.
|
||||
For example:
|
||||
|
||||
@margin-note{The term ``parameter'' is sometimes used to refer to the
|
||||
arguments of a function, but ``parameter'' in PLT Scheme
|
||||
has the more specific meaning described here.}
|
||||
|
||||
|
||||
@examples[
|
||||
(define p (make-parameter "blah"))
|
||||
(p)
|
||||
(p "meh")
|
||||
(p)]
|
||||
|
||||
Many functions (including many primitive ones) use parameters as a way
|
||||
to customize their behavior. For example @scheme[printf] will print stuff
|
||||
using the port that is the value of the @scheme[current-output-port]
|
||||
parameter. Now, say that you have some function that prints
|
||||
something:
|
||||
|
||||
@examples[
|
||||
(define (foo x) (printf "the value of x is ~s\n"))
|
||||
]
|
||||
|
||||
You usually call this function and see something printed on the screen
|
||||
-- but in some cases you want to use it to print something to a file
|
||||
or whatever. You could do this:
|
||||
|
||||
@examples[
|
||||
(define (bar)
|
||||
(let ([old-stdout (current-output-port)])
|
||||
(current-output-port my-own-port)
|
||||
(foo some-value)
|
||||
(current-output-port old-stdout)))
|
||||
]
|
||||
|
||||
One problem with this is that it is tedious to do -- but that's easily
|
||||
solved with a macro. (In fact, PLT still has a construct that does
|
||||
that in some languages: @scheme[fluid-let].) But there are more problems
|
||||
here: what happens if the call to @scheme[foo] results in a runtime error?
|
||||
This might leave the system in a bad state, where all output goes to
|
||||
your port (and you won't even see a problem, since it won't print
|
||||
anything). A solution for that (which @scheme[fluid-let] uses too) is to
|
||||
protect the saving/restoring of the parameter with @scheme[dynamic-wind],
|
||||
which makes sure that if there's an error (and more, if you know about
|
||||
continuations) then the value is still restored.
|
||||
|
||||
So the question is what's the point of having parameters instead of
|
||||
just using globals and @scheme[fluid-let]? There are two more problems that
|
||||
you cannot solve with just globals. One is what happens when you have
|
||||
multiple threads -- in this case, setting the value temporarily will
|
||||
affect other threads, which may still want to print to the standard
|
||||
output. Parameters solve this by having a specific value per-thread.
|
||||
What happens is that each thread "inherits" the value from the thread
|
||||
that created it, and changes in one thread are visible only in that
|
||||
thread.
|
||||
|
||||
The other problem is more subtle. Say that you have a parameter with
|
||||
a numeric value, and you want to do the following:
|
||||
|
||||
@examples[
|
||||
(define (foo)
|
||||
(parameterize ([p 'any-expression-goes-here])
|
||||
(foo)))
|
||||
]
|
||||
|
||||
In Scheme, "tail calls" are important -- they are the basic tool for
|
||||
creating loops and much more. @scheme[parameterize] does some magic that
|
||||
allows it to change the parameter value temporarily but still preserve
|
||||
these tail calls. For example, in the above case, you *will* get an
|
||||
infinite loop, rather than get a stack overflow error -- what happens
|
||||
is that each of these @scheme[parameterize] expressions can somehow detect
|
||||
when there's an earlier @scheme[parameterize] that no longer needs to do its
|
||||
cleanup.
|
||||
|
||||
Finally, @scheme[parameterize] actually uses two important parts of PLT to do
|
||||
its job: it uses thread cells to implement per-thread values, and it
|
||||
uses continuation marks to be able to preserve tail-calls. Each of
|
||||
these features is useful in itself.
|
||||
|
||||
@specform[(parameterize ([parameter-expr value-expr] ...)
|
||||
body ...+)]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user