#lang scribble/doc
@(require scribble/manual
          scribble/eval
          "guide-utils.ss"
          (for-label (only-in mzscheme fluid-let)))

@(define param-eval (make-base-eval))

@title[#:tag "parameterize"]{Dynamic Binding: @racket[parameterize]}

@refalso["parameters"]{@racket[parameterize]}

The @racket[parameterize] form associates a new value with a
@deftech{parameter} during the evaluation of @racket[_body]
expressions:

@specform[(parameterize ([parameter-expr value-expr] ...)
            body ...+)]

@margin-note{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 @racket[error-print-width] parameter controls how
many characters of a value are printed in an error message:

@interaction[
(parameterize ([error-print-width 5]) 
  (car (expt 10 1024)))
(parameterize ([error-print-width 10])
  (car (expt 10 1024)))
]

More generally, parameters implement a kind of dynamic binding. The
@racket[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:

@interaction[
#:eval param-eval
(define location (make-parameter "here"))
(location)
]

In a @scheme[parameterize] form, each @racket[_parameter-expr] must
produce a parameter. During the evaluation of the @scheme[body]s, each
specified parameter is given the result of the corresponding
@scheme[_value-expr]. When control leaves the @racket[parameterize]
form---either through a normal return, an exception, or some other
escape---the parameter reverts to its earlier value:

@interaction[
#:eval param-eval
(parameterize ([location "there"])
  (location))
(location)
(parameterize ([location "in a house"])
  (list (location)
        (parameterize ([location "with a mouse"])
          (location))
        (location)))
(parameterize ([location "in a box"])
  (car (location)))
(location)
]

The @scheme[parameterize] form is not a binding form like
@scheme[let]; each use of @racket[location] above refers directly to
the original definition. A @scheme[parameterize] form adjusts the
value of a parameter during the whole time that the
@scheme[parameterize] body is evaluated, even for uses of the
parameter that are textually outside of the @racket[parameterize]
body:

@interaction[
#:eval param-eval
(define (would-you-could-you?)
  (and (not (equal? (location) "here"))
       (not (equal? (location) "there"))))

(would-you-could-you?)
(parameterize ([location "on a bus"])
  (would-you-could-you?))
]

If a use of a parameter is textually inside the body of a
@racket[parameterize] but not evaluated before the
@racket[parameterize] form produces a value, then the use does not see
the value installed by the @racket[parameterize] form:

@interaction[
#:eval param-eval
(let ([get (parameterize ([location "with a fox"])
             (lambda () (location)))])
  (get))
]

The current binding of a parameter can be adjusted imperatively by
calling the parameter as a function with a value. If a
@racket[parameterize] has adjusted the value of the parameter, then
directly applying the parameter procedure affects only the value
associated with the active @racket[parameterize]:

@interaction[
#:eval param-eval
(define (try-again! where)
  (location where))

(location)
(parameterize ([location "on a train"])
  (list (location)
        (begin (try-again! "in a boat")
               (location))))
(location)
]

Using @racket[parameterize] is generally preferable to updating a
parameter value imperatively---for much the same reasons that binding
a fresh variable with @scheme[let] is preferable to using
@scheme[set!]  (see @secref["set!"]).

It may seem that variables and @racket[set!] can solve many of the
same problems that parameters solve. For example, @racket[lokation]
could be defined as a string, and @racket[set!] could be used
to adjust its value:

@interaction[
#:eval param-eval
(define lokation "here")

(define (would-ya-could-ya?)
  (and (not (equal? lokation "here"))
       (not (equal? lokation "there"))))

(set! location "on a bus")
(would-ya-could-ya?)
]

Parameters, however, offer several crucial advantages over
@scheme[set!]:

@itemlist[

 @item{The @racket[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
       @scheme[set!] is relatively tedious.}

 @item{Parameters work nicely with tail calls (see
       @secref["tail-recursion"]). The last @racket[_body] in a
       @racket[parameterize] form is in @tech{tail position} with
       respect to the @racket[parameterize] form.}

 @item{Parameters work properly with threads (see
       @refsecref["threads"]). The @scheme[parameterize] form adjusts
       the value of a parameter only for evaluation in the current
       thread, which avoids race conditions with other threads.}

]

@; ----------------------------------------

@close-eval[param-eval]
