guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Exceptions that pass continuations


From: Panicz Maciej Godek
Subject: Exceptions that pass continuations
Date: Fri, 19 Jul 2013 12:39:55 +0200

Recently I had to solve the following design problem:
I was implementing a high-level wrapper for OpenGL 
lights. In order to use the lights, I had to allocate them 
before drawing an OpenGL scene, and then release 
them after the scene has been drawn.

Initially, I wrote a comment in the function that allocated
the lights that they need to be deallocated. I though,
however, that this is insufficient, because if I ever decided
to reuse the function that allocated the light, I'd need to
read its source code again (which isn't a common practice
-- when I use a function, I'm only interested in what the
function is doing, and not -- how it's doing that)

Instead I finally decided to guarantee that the caller
is at least aware that the light needs to be deallocated,
so that even if it decides to ignore the deallocation, it
does so consciously.

I came up with the idea to throw an exception with current
continuation. The caller can do whatever is needed to later
release the resources, and then call the received continuation.

I wrapped it in the following constructs:

(define (demand to-do-something-with . args)
  (call/cc (lambda(go-on)
             (apply throw 'demand go-on to-do-something-with args)))
  ;; for some reason, the code fails to work without the following
  ;; empty (begin) form
  (begin))

(define-syntax supply
  (syntax-rules ()
    ((_ (((do-something-with . args) do-what ...) ...)
        actions ...)
     (let ((handlers (make-hash-table))
           (unsupported (lambda details
                          (apply throw 'unsupported-reminder
                                 details))))
       (hash-set! handlers (quote do-something-with)
                  (lambda args do-what ...))
       ...
       (catch 'demand
         (lambda () actions ...)
         (lambda (key go-on memorandum . subjects)
           (apply (hash-ref handlers memorandum unsupported) subjects)
           (go-on)))))))

and the whole thing can be used as follows

(let ((resources '()))
  (supply (((release-resource r)
               (set! r (cons r resources))))
    (let ((r (allocate-resource)))
      (demand 'release-resource r)
      (do-something-constructive-with r)))
  (for-each release-resource resources))

(of course, this makes little sense if the
resource is released within the same
procedure it is allocated, but if the release
cannot be performed locally, it seems the
right thing)

I wonder whether this design pattern has ever
been used before, or if there are any potential
flaws with its application.

The advantage is that it reduces the congnitive
load of the programmer: there's less to remember,
because unsatisfied demands remind about
themselves.

Regards

reply via email to

[Prev in Thread] Current Thread [Next in Thread]