The built-in dynamic-wind procedure is Scheme’s equivalent to Common Lisp’s unwind-protect, the try ... finally ... construct in languages like C# and Java and so on.

The main entry point for this example is a provedure named drill-hole. It uses the simulated drill press described earlier. The drill-hole function defines several private functions including start-drilling, keep-drilling and stop-drilling that are used in the arguments passed to dynamic-wind in the body of drill-hole. As noted in the chapter on [lexical closures][] where [make-drill][] was defined, the simulated drill press has a number of run-time constraints such as that the drill motor can only be started or stopped when the drill height is 0. The drill-hole procedure is a wrapper for an instance of such a simulated drill that uses dynamic-wind to protect the wrapped drill from violations of such constraints even when its keep-drilling loop is exited and re-entered using a continuation.

The result of calling keep-drilling is bound to the local variable k in the body of drill-hole. That call to keep-drilling is protected during stack winding and unwinding by calls to start-drilling and stop-drilling, respectively, by passing all three functions in a call to dynamic-wind.

Those familiar with languages with constructs like (unwind-protect ...) or try ... finally ... can understand the relationship between keep-drilling and stop-drilling as being an enhanced version of such constructs.

In other words, dynamic-wind guarantees to call stop-drilling after keep-drilling exits, whether or not the latter exits normally or through some non-sequential flow of control, as in the following Common Lisp code:




Or in the case of languages like Java and C#, something like:

try {


} finally {



In C++, similar stack-unwinding protections are achieved using “automatic” variables of types with destructors.

The additional functionality offered by dynamic-wind over the unwind-protect and try ... finally ... examples is that dynamic-wind also guarantees that start-drilling will be called before keep-drilling starts execution the first time it is entered and if it is re-entered by invoking a continuation after it has previously exited.

This stack winding / unwinding / rewinding behavior is demonstrated in the body of drill-hole. In particular, keep-drilling bores the hole 50% through the full range of the possible bit positions and then exits, returning a continuation that is later used to resume drilling until the hole is 100% complete. Looking at the output, one can see that start-drilling and stop-drilling are called the appropriate number of times (twice each), in the appropriate places in the overall flow of control (each time the body of keep-drilling is about to be entered and after each time keep-drilling exits, respectively).

Here is the Scheme source for drill-hole:

;; -*- geiser-scheme-implementation: guile -*-

;; Copyright 2016 Kirk Rader
;; Licensed under the Apache License, Version 2.0 (the "License"); you
;; may not use this file except in compliance with the License.  You
;; may obtain a copy of the License at
;; http://www.apache.org/licenses/LICENSE-2.0
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; implied.  See the License for the specific language governing
;; permissions and limitations under the License.

;; A Scheme program that illustrates the use of dynamic-wind to
;; protect entry, exit and re-entry of an execution context using
;; continuations.
;; This uses an object that simulates an automated drill press. The
;; simulated drill has various operational constraints, such as that
;; the main drilling motor can only be turned on or off when the bit
;; is in its highest position.
;; The point of the example is to show how dynamic-wind can be used to
;; ensure that these constraints are taken into account by an ongoing
;; sequence of drilling commands even when that sequence might be
;; exited and then re-entered using continuations.

;; Function: (drill-hole)
;; Usage: (drill-hole)
;; Outputs a sequence of messages to the console indicating a drill's
;; state changes over the course of a program to bore a hole.
;; This uses dynamic-wind to ensure that the drilling motor is safely
;; started and stopped each time the drilling program execution
;; context is entered, exited or re-entered using continuations.
;; Returns a value that indicates the outcome of the drilling program.
(define (drill-hole)

  (let ((drill (make-drill)))

    (letrec ((start-drilling
              ;; turn on the motor
              ;; assumes that motor is currently stopped and that bit
              ;; position is 0
              ;; this will be used as the "before thunk" in a call to
              ;; dynamic-wind
              (lambda ()
                (display "start-drilling called")
                (drill 'start-motor!)))

              ;; raise the bit and stop the motor
              ;; assumes that motor is currently running
              ;; this will be used as the "after thunk" in a call to
              ;; dynamic-wind
              (lambda ()
                (display "stop-drilling called")
                (drill 'move-bit! 0)
                (drill 'stop-motor!)))

              ;; the drilling program
              ;; assumes the motor is currently running
              ;; stops half-way through to let the bit cool, returning a
              ;; continuation by which drilling can be resumed and
              ;; continue to completion
              ;; returns 'done when the hole is completely drilled
              (lambda ()
                 (lambda (return)
                   (display "keep-drilling entered")
                   (drill 'move-bit! 50)
                   (display "hole 50% drilled, pausing to let bit cool")
                   (let ((command (call/cc (lambda (k) (return k)))))
                     (display "keep-drilling re-entered")
                     (if (eq? command 'continue)
                           (display "continuing to drill")
                           (drill 'move-bit! 100)
                           (display "hole completely drilled")
                           (display "drilling program canceled")
              ;; bind k to the result of a call to keep-drilling,
              ;; protected during stack winding and unwinding by calls to
              ;; start-drilling and stop-drilling, respectively

                ;; ensure drill motor is running every time keep-drilling
                ;; is entered or re-entered

                ;; drill the hole

                ;; ensure drill motor is stopped every time keep-drilling
                ;; exits

      ;; we get here twice:
      ;; 1. when keep-drilling returns a continuation half-way through
      ;; the drilling process
      ;; 2. when keep-drilling ultimately completes as a result of the
      ;; invocation of k, below
      ;; in *both* cases, the preceding call to dynamic-wind ensures
      ;; that start-drilling is called before the body of
      ;; keep-drilling is entered and stop-drilling is called after
      ;; the body of keep-drilling exits, even though those entries
      ;; and exits happen at different times and at different points
      ;; in the flow of control

      (if (procedure? k)

          ;; k is a continuation procedure when keep-drilling exits
          ;; half-way throuh the drilling process
            (display "waiting a bit (pun intended)")
            ;; use k to resume drilling
            ;; keep-drilling will not exit again until the hole is
            ;; completely bored unless an exception is thrown
            (k 'continue))

          ;; when keep-drilling finishes boring the hole completely, it
          ;; returns a value that is not a procedure
          ;; return that final value as the result of drill-hole

(download drill-hole.scm)

Here is the output of (drill-hole):

start-drilling called
started motor
keep-drilling entered
stepping bit position from 0 to 50 by 1
hole 50% drilled, pausing to let bit cool
stop-drilling called
stepping bit position from 50 to 0 by -1
motor stopped
waiting a bit (pun intended)
start-drilling called
started motor
keep-drilling re-entered
continuing to drill
stepping bit position from 0 to 100 by 1
hole completely drilled
stop-drilling called
stepping bit position from 100 to 0 by -1
motor stopped