[plt-scheme] Re: "unwind-protect" for PLT Scheme?

From: Eli Barzilay (eli at barzilay.org)
Date: Sun Jul 5 01:50:37 EDT 2009

On Jul  4, Shriram Krishnamurthi wrote:
> You only need the before-thunk argument to do something if you have
> continuations somewhere deep within the body expression.  (But note
> that even if you don't use continuations, you just might use a
> library function that does.)  As long as neither you nor a library
> does, you can pass something trivial as before-thunk, such as void.
> That reduces to being the same as Lisp's unwind-protect.

Sorry to add another post on this, but I'll try to make a quick
summary of this whole thing in plain English (as much as I can call
what *I* write "plain English"):

* When no continuations are involved, `dynamic-wind' is perfectly fine
  as is, and you can set up the resource either in the before thunk,
  or before the whole call:

    (let ([foo (open-output-file ...)])
      (dynamic-wind void
                    (lambda () ...do stuff with foo...)
                    (lambda () ...close foo...)))


    (let ([foo #f])
      (dynamic-wind (lambda () (set! foo (open-output-file ...)))
                    (lambda () ...do stuff with foo...)
                    (lambda () ...close foo...)))

* When continuations are involved, then control can jump into or out
  of the main thunk, and you have several options to choose from.
  Here the before thunk becomes important since it can be called not
  just once, but several times -- once when it starts, and a pair of
  after-before calls for each jump out of and back into the main

  Just ignoring this makes it possible for your code to run into
  problems that are similar in nature to writing code that is not
  thread-safe: in both cases the problem is not in your own code, but
  in the way it is being called or in the way library calls it
  performs behave.  And this analogy carries over to how people deal
  with it: lots of user code just ignores the whole issue, but you
  need to be more careful if you're writing some library, or parts of
  the core language.  [`call/cc' is notorious in exposing all kinds of
  things about how things are implemented (for example, IIRC there was
  a point in the (distant?) past where you could squeeze two different
  behaviors out of mzscheme's `map' function because the
  implementation was optimizing uses with a few functions).]

* Options for dealing with this are:

  - Ignore the whole thing, and use the first form above.

  - Arrange for the resource to be properly opened and closed on the
    before/after thunks -- which means that you need to make sure
    that, for example, you open the file in `append' mode so when
    jumping out and back in the resource will be recreated.  Of
    course, this depends on the code doing something simple, like just
    writing to the file.

  - Forbid re-entry: you make a before thunk that remembers if it was
    already called -- if that's the case, then you know that somehow
    control went out and back in, and you make it throw an appropriate
    error.  This can be convenient if, for example, it is meaningless
    to use the resource after it was already released.

  - Intentionally avoid creating and releasing the resource in a
    `dynamic-wind', so it will only be released when control leaves
    normally.  An example for this would be:

      (let ([foo (open-output-file ...)])
        (begin0 (...do stuff with foo...)
          (...close foo...)))

    or better:

      (let ([foo (open-output-file ...)])
        (define (close-foo) ...close foo...)
        (with-handlers ([(lambda (x) #t)
                         (lambda (e) (close-foo) (raise e))])
          (begin0 (...do stuff with foo...)

    This works if when control happens to go out, it will also go back
    in, so eventually control leaves as usual.

  - There's a number of variants on these and subtle points involved

          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the users mailing list.