Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

HANDLER-CASE* - HANDLER-BIND and HANDLER-CASE rolled into one. #56

Open
heegaiximephoomeeghahyaiseekh opened this issue Apr 3, 2016 · 0 comments

Comments

@heegaiximephoomeeghahyaiseekh

;; Write the code here.

(defun expand-handler-bind (type lambda-list &key before-unwind after-unwind)
    "Helper for the HANDLER-CASE* macro. This one creates the HANDLER-BIND lambdas out of the :BEFORE-UNWIND form."
    (declare (ignorable after-unwind))
    (when before-unwind
      (when (null lambda-list)
        (error ":BEFORE-UNWIND must bind the condtion to a variable."))
      `(,type (lambda ,lambda-list (declare (ignorable ,@lambda-list)) ,before-unwind))))

(defun expand-handler-case (type lambda-list &key before-unwind after-unwind)
  "Helper for the HANDLER-CASE* macro. This one creates the HANDLER-CASE handler-clauses out of the :AFTER-UNWIND form"
  (declare (ignorable before-unwind))
  (if after-unwind
      `(,type ,lambda-list (declare (ignorable ,@lambda-list)) ,after-unwind))))

(defmacro handler-case* (form &rest cases)
  "Like HANDLER-CASE and HANDLER-BIND rolled into one. Example usage:

      (handler-case*
            (restart-case
                 (error \"ZOMG! ERROR!\")
               (fuck-it () 'ignored))
         (t (condition)
            :before-unwind
               (progn (format t \"An error occurred Ignore it (y/N)? \")
                      (if (eq (read) 'y)
                          (invoke-restart 'fuck-it)))
            :after-unwind
               (format t \"You just couldn't fucking ignore it, could you?~%\")))

:before-unwind is, of course, executed before the stack unrolls, so you can
invoke restarts from there. If no restart is invoked, the error falls through
to the :after-unwind case, where you can handle it like a regular handler-case.

The above paragraph is not 100% accurate: If the :BEFORE-UNWIND case does not
invoke a restart, another HANDLER-CASE or HANDLER-CASE* form may get control
of the error *before* the :AFTER-UNWIND case:

    (defun handles-own-error ()
        (handler-case
            (error \"ZOMG! ERROR!\")
         (t () :i-got-this)))

      (handler-case*
            (restart-case
                 (handles-own-error)
               (fuck-it () 'ignored))
         (t (condition)
            :before-unwind
               (progn (format t \"An error occurred Ignore it (y/N)? \")
                      (if (eq (read) 'y)
                          (invoke-restart 'fuck-it)))
            :after-unwind
               (format t \"You just couldn't fucking ignore it, could you?~%\")))

In this case, first the :BEFORE-UNWIND case is evaluated, and then the error is given
to the HANDLER-CASE form *inside* HANDLES-OWN-ERROR. The :AFTER-UNWIND case is never
evaluated.

If no :after-unwind form is provided and no restart is invoked, the condition is not trapped."
  `(handler-case
       (handler-bind ,(remove nil (mapply #'expand-handler-bind cases))
         ,form)
     ,@(remove nil (mapply #'expand-handler-case cases))))

Provides: HANDLER-CASE*
Requires: MAPPLY
Author: Jeremy Phelps phelpsj@nuvox.net
License: Public Domain

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants