You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
typesetting/quad/qtest/mds/parameterize.md

152 lines
5.9 KiB
Markdown

5 years ago
# Dynamic Binding: `parameterize`
> +\[missing\] in \[missing\] also documents `parameterize`.
The `parameterize` form associates a new value with a _parameter_ during
the evaluation of `body` expressions:
```racket
(parameterize ([parameter-expr value-expr] ...)
body ...+)
```
> The term “parameter” is sometimes used to refer to the arguments of a
> function, but “parameter” in Racket has the more specific meaning
> described here.
For example, the `error-print-width` parameter controls how many
characters of a value are printed in an error message:
```racket
> (parameterize ([error-print-width 5])
(car (expt 10 1024)))
car: contract violation
expected: pair?
given: 10...
> (parameterize ([error-print-width 10])
(car (expt 10 1024)))
car: contract violation
expected: pair?
given: 1000000...
```
More generally, parameters implement a kind of dynamic binding. The
`make-parameter` function takes any value and returns a new parameter
that is initialized to the given value. Applying the parameter as a
function returns its current value:
```racket
> (define location (make-parameter "here"))
> (location)
"here"
```
In a `parameterize` form, each `parameter-expr` must produce a
parameter. During the evaluation of the `body`s, each specified
parameter is given the result of the corresponding `value-expr`. When
control leaves the `parameterize` form—either through a normal return,
an exception, or some other escape—the parameter reverts to its earlier
value:
```racket
> (parameterize ([location "there"])
(location))
"there"
> (location)
"here"
> (parameterize ([location "in a house"])
(list (location)
(parameterize ([location "with a mouse"])
(location))
(location)))
'("in a house" "with a mouse" "in a house")
> (parameterize ([location "in a box"])
(car (location)))
car: contract violation
expected: pair?
given: "in a box"
> (location)
"here"
```
The `parameterize` form is not a binding form like `let`; each use of
`location` above refers directly to the original definition. A
`parameterize` form adjusts the value of a parameter during the whole
time that the `parameterize` body is evaluated, even for uses of the
parameter that are textually outside of the `parameterize` body:
```racket
> (define (would-you-could-you?)
(and (not (equal? (location) "here"))
(not (equal? (location) "there"))))
> (would-you-could-you?)
#f
> (parameterize ([location "on a bus"])
(would-you-could-you?))
#t
```
If a use of a parameter is textually inside the body of a `parameterize`
but not evaluated before the `parameterize` form produces a value, then
the use does not see the value installed by the `parameterize` form:
```racket
> (let ([get (parameterize ([location "with a fox"])
(lambda () (location)))])
(get))
"here"
```
The current binding of a parameter can be adjusted imperatively by
calling the parameter as a function with a value. If a `parameterize`
has adjusted the value of the parameter, then directly applying the
parameter procedure affects only the value associated with the active
`parameterize`:
```racket
> (define (try-again! where)
(location where))
> (location)
"here"
> (parameterize ([location "on a train"])
(list (location)
(begin (try-again! "in a boat")
(location))))
'("on a train" "in a boat")
> (location)
"here"
```
Using `parameterize` is generally preferable to updating a parameter
value imperatively—for much the same reasons that binding a fresh
variable with `let` is preferable to using `set!` \(see \[missing\]\).
It may seem that variables and `set!` can solve many of the same
problems that parameters solve. For example, `lokation` could be defined
as a string, and `set!` could be used to adjust its value:
```racket
> (define lokation "here")
> (define (would-ya-could-ya?)
(and (not (equal? lokation "here"))
(not (equal? lokation "there"))))
> (set! lokation "on a bus")
> (would-ya-could-ya?)
#t
```
Parameters, however, offer several crucial advantages over `set!`:
* The `parameterize` form helps automatically reset the value of a
parameter when control escapes due to an exception. Adding exception
handlers and other forms to rewind a `set!` is relatively tedious.
* Parameters work nicely with tail calls \(see \[missing\]\). The last
`body` in a `parameterize` form is in tail position with respect to
the `parameterize` form.
* Parameters work properly with threads \(see \[missing\]\). The
`parameterize` form adjusts the value of a parameter only for
evaluation in the current thread, which avoids race conditions with
other threads.