(lispkit dynamic)

Dynamic bindings

Returns a newly allocated parameter object, which is a procedure that accepts zero arguments and returns the value associated with the parameter object. Initially, this value is the value of (converter init), or of init if the conversion procedure converter is not specified. The associated value can be temporarily changed using parameterize. The default associated value can be changed by invoking the parameter object as a function with the new value as the only argument.

Parameter objects can be used to specify configurable settings for a computation without the need to pass the value to every procedure in the call chain explicitly.

A parameterize expression is used to change the values returned by specified parameter objects param during the evaluation of body. The param and value expressions are evaluated in an unspecified order. The body is evaluated in a dynamic environment in which calls to the parameters return the results of passing the corresponding values to the conversion procedure specified when the parameters were created. Then the previous values of the parameters are restored without passing them to the conversion procedure. The results of the last expression in the body are returned as the results of the entire parameterize expression.

(define radix
  (make-parameter 10 (lambda (x)
                       (if (and (exact-integer? x) (<= 2 x 16))
                           x
                           (error "invalid radix")))))
(define (f n) (number->string n (radix)))
(f 12)                              ⇒  "12"
(parameterize ((radix 2)) (f 12))   ⇒  "1100"
(f 12)                              ⇒  "12"
(radix 16)
(parameterize ((radix 0)) (f 12))   ⇒  error: invalid radix

Returns a newly allocated copy of the current dynamic environment. Dynamic environments are represented as mutable hashtables.

Returns the current dynamic environment represented as mutable hashtables.

Sets the current dynamic environment to the given dynamic environment object. Dynamic environments are modeled as hashtables.

Continuations

Returns #t if obj is a continuation procedure, #f otherwise.

The procedure call-with-current-continuation (or its equivalent abbreviation call/cc) packages the current continuation as an “escape procedure” and passes it as an argument to proc. It is an error if proc does not accept one argument.

The escape procedure is a Scheme procedure that, if it is later called, will abandon whatever continuation is in effect at that later time and will instead use the continuation that was in effect when the escape procedure was created. Calling the escape procedure will cause the invocation of before and after thunks installed using dynamic-wind.

The escape procedure accepts the same number of arguments as the continuation to the original call to call-with-current-continuation. Most continuations take only one value. Continuations created by the call-with-values procedure (including the initialization expressions of define-values, let-values, and let*-values expressions), take the number of values that the consumer expects. The continuations of all non-final expressions within a sequence of expressions, such as in lambda, case-lambda, begin, let, let*, letrec, letrec*, let-values, let*-values, let-syntax, letrec-syntax, parameterize, guard, case, cond, when, and unless expressions, take an arbitrary number of values because they discard the values passed to them in any event. The effect of passing no val- ues or more than one value to continuations that were not created in one of these ways is unspecified.

The escape procedure that is passed to proc has unlimited extent just like any other procedure in Scheme. It can be stored in variables or data structures and can be called as many times as desired. However, like the raise and error procedures, it never returns to its caller.

The following examples show only the simplest ways in which call-with-current-continuation is used. If all real uses were as simple as these examples, there would be no need for a procedure with the power of call-with-current-continuation.

(call-with-current-continuation
  (lambda (exit)
    (for-each (lambda (x) (if (negative? x) (exit x)))
              '(54 0 37 -3 245 19)) #t))  ⇒  -3
(define list-length
  (lambda (obj)
    (call-with-current-continuation
      (lambda (return)
        (letrec
          ((r (lambda (obj)
                (cond ((null? obj) 0)
                      ((pair? obj) (+ (r (cdr obj)) 1))
                      (else        (return #f))))))
          (r obj))))))
(list-length '(1 2 3 4))     ⇒  4
(list-length '(a b . c))     ⇒  #f

Calls thunk without arguments, returning the result(s) of this call. before and after are called, also without arguments, as required by the following rules. Note that, in the absence of calls to continuations captured using call-with-current-continuation, the three arguments are called once each, in order. before is called whenever execution enters the dynamic extent of the call to thunk and after is called whenever it exits that dynamic extent. The dynamic extent of a procedure call is the period between when the call is initiated and when it returns. The before and after thunks are called in the same dynamic environment as the call to dynamic-wind. In Scheme, because of call-with-current-continuation, the dynamic extent of a call is not always a single, connected time period. It is defined as follows:

  • The dynamic extent is entered when execution of the body of the called procedure begins.

  • The dynamic extent is also entered when execution is not within the dynamic extent and a continuation is invoked that was captured (using call-with-current-continuation) during the dynamic extent.

  • It is exited when the called procedure returns.

  • It is also exited when execution is within the dynamic extent and a continuation is invoked that was captured while not within the dynamic extent.

If a second call to dynamic-wind occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the afters from these two invocations of dynamic-wind are both to be called, then the after associated with the second (inner) call to dynamic-wind is called first.

If a second call to dynamic-wind occurs within the dynamic extent of the call to thunk and then a continuation is invoked in such a way that the befores from these two invocations of dynamic-wind are both to be called, then the before associated with the first (outer) call to dynamic-wind is called first.

If invoking a continuation requires calling the before from one call to dynamic-wind and the after from another, then the after is called first.

The effect of using a captured continuation to enter or exit the dynamic extent of a call to before or after is unspecified.

(let ((path ’())
      (c #f))
  (let ((add (lambda (s)
        (set! path (cons s path)))))
    (dynamic-wind
      (lambda () (add 'connect))
      (lambda () (add (call-with-current-continuation
                   (lambda (c0) (set! c c0) 'talk1))))
      (lambda () (add 'disconnect)))
    (if (< (length path) 4)
        (c 'talk2)
        (reverse path))))
  ⇒  (connect talk1 disconnect connect talk2 disconnect)

Executes expression body guaranteeing that statements cleanup ... are executed when body's execution is finished or when an exception is thrown during the execution of body. unwind-protect returns the result of executing body.

Returns to the top-level of the read-eval-print loop with obj as the result (or terminates the program with obj as its return value).

Exceptions

The with-exception-handler procedure returns the results of invoking thunk. handler is installed as the current exception handler in the dynamic environment used for the invocation of thunk. It is an error if handler does not accept one argument. It is also an error if thunk does not accept zero arguments.

(call-with-current-continuation
  (lambda (k)
    (with-exception-handler
      (lambda (x)
        (display "condition: ")(write x)(newline)(k 'exception))
      (lambda ()
        (+ 1 (raise 'an-error))))))  ⇒ exception; prints "condition: an-error"
(with-exception-handler
  (lambda (x) (display "something went wrong\n"))
  (lambda () (+ 1 (raise 'an-error))))   ⇒ prints "something went wrong"

After printing, the second example then raises another exception: "exception handler returned".

try executes argument-less procedure thunk and returns the result as the result of try if thunk's execution completes normally. If an exception is thrown, procedure handler is called with the exception object as its argument. The result of executing handler is returned by try.

The body is evaluated with an exception handler that binds the raised object to var and, within the scope of that binding, evaluates the clauses as if they were the clauses of a cond expression. That implicit cond expression is evaluated with the continuation and dynamic environment of the guard expression. If every cond-clause’s test evaluates to #f and there is no "else" clause, then raise-continuable is invoked on the raised object within the dynamic environment of the original call to raise or raise-continuable, except that the current exception handler is that of the guard expression.

Please note that each cond-clause is as in the specification of cond.

(guard (condition
         ((assq ’a condition) => cdr)
         ((assq ’b condition)))
  (raise (list (cons ’a 42))))         ⇒  42
(guard (condition
         ((assq ’a condition) => cdr)
         ((assq ’b condition)))
  (raise (list (cons ’b 23))))         ⇒ (b . 23)

Returns a newly allocated custom error object consisting of message as its error message and the list of irritants irrlist.

Returns a newly allocated assertion error object referring to a procedure of name procname and an expression expr which triggered the assertion. Assertion errors that were raised should never be caught as they indicate a violation of an invariant.

Raises an exception by invoking the current exception handler on obj. The handler is called with the same dynamic environment as that of the call to raise, except that the current exception handler is the one that was in place when the handler being called was installed. If the handler returns, a secondary exception is raised in the same dynamic environment as the handler. The relationship between obj and the object raised by the secondary exception is unspecified.

Raises an exception by invoking the current exception handler on obj. The handler is called with the same dynamic environment as the call to raise-continuable, except that: (1) the current exception handler is the one that was in place when the handler being called was installed, and (2) if the handler being called returns, then it will again become the current exception handler. If the handler returns, the values it returns become the values returned by the call to raise-continuable.

(with-exception-handler
  (lambda (con)
    (cond ((string? con) (display con))
          (else          (display "a warning has been issued")))
    42)
  (lambda ()
    (+ (raise-continuable "should be a number") 23)))
prints: should be a number
  ⇒  65

Raises an exception as if by calling raise on a newly allocated error object which encapsulates the information provided by message, as well as any obj, known as the irritants. The procedure error-object? must return #t on such objects. message is required to be a string.

(define (null-list? l)
  (cond ((pair? l) #f)
        ((null? l) #t)
        (else (error "null-list?: argument out of domain" l))))

Raises an exception as if by calling raise on a newly allocated assertion error object encapsulating expr as the expression which triggered the assertion failure and the current procedure's name. Assertion errors that are raised via assertion should never be caught as they indicate a violation of a critical invariant.

(define (null-list? l)
  (cond ((pair? l) #f)
        ((null? l) #t)
        (else (assertion '(list? l)))))

Executes expr0, expr1, ... in the given order and raises an assertion error as soon as the first expression is evaluating to #f. The raised assertion error encapsulates the expression that evaluated to #f and the name of the procedure in which the assert statement was placed.

(define (drop-elements xs n)
  (assert (list? xs) (fixnum? n) (not (negative? n)))
  (if (or (null? xs) (zero? n)) xs (drop-elements (cdr xs) (fx1- n))))

Returns #t if obj is an error object, #f otherwise. Error objects are either implicitly created via error or they are created explicitly with procedure make-error.

Returns the message (which is a string) encapsulated by the error object err.

Returns a list of the irritants encapsulated by the error object err.

Returns a list of procedures representing the stack trace encapsulated by the error object err. The stack trace reflects the currently active procedures at the time the error object was created (either implicitly via error or explicitly via make-error).

This error type predicate returns #t if obj is an error object raised by the read procedure; otherwise, it returns #f.

This error type predicate returns #t if obj is an error object raised by the inability to open an input or output port on a file; otherwise, it returns #f.

Exiting

Runs all outstanding dynamic-wind after procedures, terminates the running program, and communicates an exit value to the operating system. If no argument is supplied, or if obj is #t, the exit procedure should communicate to the operating system that the program exited normally. If obj is #f, the exit procedure will communicate to the operating system that the program exited abnormally. Otherwise, exit should translate obj into an appropriate exit value for the operating system, if possible. The exit procedure must not signal an exception or return to its continuation.

Terminates the program without running any outstanding dynamic-wind "after procedures" and communicates an exit value to the operating system in the same manner as exit.

Last updated