#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]