[alexandria-devel] UNWIND-PROTECT-CASE
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
"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.
participants (3)
-
Attila Lendvai
-
Nikodemus Siivola
-
Tobias C. Rittweiler