Sometimes it's desired to only do a certain cleanup operating in an UNWIND-PROTECT if a true transfer-of-control was issued in the protected-form.
UNWIND-PROTECT-CASE can be used to conveniently control on what circumstances cleanup operations are supposed to be performed.
(I think I've seen UNWIND-PROTECT-CASE on one of the Lisp machines, so it's actually prior art.)
-T.
(defmacro unwind-protect-case ((&optional abort-flag) protected-form &body clauses) "Like CL:UNWIND-PROTECT, but you can specify the circumstances that the cleanup CLAUSES are run.
ABORT-FLAG is the name of a variable that will be bound to T in CLAUSES if the PROTECTED-FORM aborted preemptively, and to NIL otherwise.
Examples:
(unwind-protect-case () (protected-form) (:normal (format t "This is only evaluated if PROTECTED-FORM executed normally.~%")) (:abort (format t "This is only evaluated if PROTECTED-FORM aborted preemptively.~%")) (:always (format t "This is evaluated in either case.~%")))
(unwind-protect-case (aborted-p) (protected-form) (:always (perform-cleanup-if aborted-p))) " (check-type abort-flag (or null symbol)) (let ((gflag (gensym "FLAG+"))) `(let ((,gflag t)) (unwind-protect (multiple-value-prog1 ,protected-form (setf ,gflag nil)) (let ,(and abort-flag `((,abort-flag ,gflag))) ,@(loop for (cleanup-kind . forms) in clauses collect (ecase cleanup-kind (:normal `(when (not ,gflag) ,@forms)) (:abort `(when ,gflag ,@forms)) (:always `(progn ,@forms)))))))))
UNWIND-PROTECT-CASE can be used to conveniently control on what circumstances cleanup operations are supposed to be performed.
+1 from me.
i'd be happy to push this, but imho the extra argument is unnecessary. i understand that it is a tiny bit more flexible in rare situations like:
(unwind-protect-case (aborted-p) (random 42) (:always (when aborted-p ...)))
but those are mostly covered by the three :always, :normal, :abort cases.
and it introduces an extra argument for unwind-protect-case which makes it look less like unwind-protect.
these are my 0.02, but i won't do anything on my own in this question unless others express their opinions, too.
"Attila Lendvai" attila.lendvai@gmail.com writes:
i'd be happy to push this, but imho the extra argument is unnecessary. i understand that it is a tiny bit more flexible in rare situations like:
(unwind-protect-case (aborted-p) (random 42) (:always (when aborted-p ...)))
but those are mostly covered by the three :always, :normal, :abort cases.
It's not for this because you can write this one as
(unwind-protect-case () (...) (:always (do-something)) (:abort (handle-abort)) (:always (do-something-else)))
It's for the case where you want to _delegate_ the work to a function:
(unwind-protect-case (aborted-p) (...) (:always (perform-cleanup aborted-p ...)))
Where PERFORM-CLEANUP does different things depending on the ABORTED-P flag.
The reason for this is for the case where you want to use UNWIND-PROTECT-CASE in a macroexpansion, and don't want to blow up the macroexpansion with too much code.
and it introduces an extra argument for unwind-protect-case which makes it look less like unwind-protect.
I actually consider it to be a feature because it's easier to distinguish these two syntactically.
-T.
On Fri, Mar 14, 2008 at 3:05 PM, Tobias C. Rittweiler tcr@freebits.de wrote:
I actually consider it to be a feature because it's easier to distinguish these two syntactically.
Makes sense to me, +1!
Cheers,
-- Nikodemus
alexandria-devel@common-lisp.net