Skip to main content

progv

progv Special Operator

Syntax:

progv symbols values {form}* → {result}*

Arguments and Values:

symbols—a list of symbols; evaluated.

values—a list of objects; evaluated.

forms—an implicit progn.

results—the values returned by the forms.

Description:

progv creates new dynamic variable bindings and executes each form using those bindings. Each form is evaluated in order.

progv allows binding one or more dynamic variables whose names may be determined at run time. Each form is evaluated in order with the dynamic variables whose names are in symbols bound to corresponding values. If too few values are supplied, the remaining symbols are bound and then made to have no value. If too many values are supplied, the excess values are ignored. The bindings of the dynamic variables are undone on exit from progv.

Examples:

(setq \*x\* 1)1 
(progv(\*x\*)(2) \*x\*)2
\*x\* → 1
Assuming \*x\* is not globally special,
(let ((\*x\* 3))
(progv(\*x\*)(4)
(list \*x\* (symbol-value ’\*x\*))))(3 4)


See Also:

let, Section 3.1 (Evaluation)

Notes:

Among other things, progv is useful when writing interpreters for languages embedded in Lisp; it provides a handle on the mechanism for binding dynamic variables.

Expanded Reference: progv

Basic Dynamic Binding

progv dynamically binds a list of symbols to a list of values for the duration of its body. Unlike let, the variable names are evaluated at runtime, making progv useful for dynamic/computed bindings.

(defvar *x* 10)

(progv '(*x*) '(42)
*x*)
=> 42

;; After progv, the old binding is restored
*x*
=> 10

Binding Multiple Variables

progv can bind multiple variables at once. The symbols and values lists are matched positionally.

(defvar *a* 1)
(defvar *b* 2)

(progv '(*a* *b*) '(100 200)
(list *a* *b*))
=> (100 200)

;; Original values are restored
(list *a* *b*)
=> (1 2)

Runtime-Determined Variable Names

Unlike let, the symbol names in progv are evaluated, so you can compute which variables to bind at runtime.

(defvar *debug-level* 0)
(defvar *verbose* nil)

(defun with-config (settings thunk)
"Bind dynamic variables from an alist of (symbol . value) pairs."
(progv (mapcar #'car settings)
(mapcar #'cdr settings)
(funcall thunk)))

(with-config '((*debug-level* . 3) (*verbose* . t))
(lambda () (list *debug-level* *verbose*)))
=> (3 T)

Fewer Values Than Symbols

If there are more symbols than values, the extra symbols become unbound (not nil) within the body.

(defvar *p* :original)

(handler-case
(progv '(*p*) '()
*p*)
(unbound-variable (c)
(format nil "~A is unbound" (cell-error-name c))))
=> "*P* is unbound"

;; Restored after progv
*p*
=> :ORIGINAL