It's not currently possible to define new Java Exceptions without
ahead-of-time compilation, which is sometimes inconvenient.  Besides
this, Java's try/catch exception system isn't as flexible as, for
example, Common Lisp's condition/restart system.

To address both these concerns, I've added a lib called error-kit to
clojure-contrib.  Its still pretty experimental, so if you find it
awkward to use of think of better names of the lib itself of anything
it defines, please let me know.  Of course I'd also love to hear if
anyone finds it useful.

Here's a quick demo of its features:

  (require '(clojure.contrib [error-kit :as kit]))

This defines an error and its action if unhandled.  A good choice of
unhandled action is to throw a Java exception so users of your code
who do not want to use error-kit can still use normal Java try/catch
forms to handle the error.  The throw-msg macro helps you do this:

  (kit/deferror *number-error* [] [n]
    {:msg (str "Number error: " n)
     :unhandled (kit/throw-msg NumberFormatException)})

This next error derives from the one above, which means if
*odd-number-error* is raised, it can be caught by a handler for
*number-error*:

  (kit/deferror *odd-number-error* [*number-error*]
    "Indicates an odd number was given to an operation that is only
    defined for even numbers."
    [n]
    {:msg (str "Can't handle odd number: " n)})

Raise an error by name with any extra args as defined by the deferror:

  (defn int-half [i]
    (if (even? i)
      (quot i 2)
      (kit/raise *odd-number-error* i)))

Since this form ends up calling 'raise', but has no 'with-hander' and
'handle' form, it will throw a regular Java NumberFormatException:

  (vec (map int-half [2 4 5 8]))

But you can handle the error, collect details from the args passed to
'raise', and do whatever you want with it.  In this case, just throw
an Exception with a more specific message:

  (kit/with-handler
    (vec (map int-half [2 4 5 8]))
    (kit/handle *odd-number-error* [n]
      (throw (Exception. (format "Odd number %d in vector." n)))))

The above is equivalent to the more complicated version below:

  (kit/with-handler
    (vec (map int-half [2 4 5 8]))
    (kit/handle {:keys [n tag]}
      (if (isa? tag `*odd-number-error*)
        (throw (Exception. (format "Odd number %d in vector." n)))
        (kit/do-not-handle))))

Returns "invalid" string instead of a vector when an error is
encountered.  This is like Clojure's try/catch form.

  (kit/with-handler
    (vec (map int-half [2 4 5 8]))
    (kit/handle kit/*error* [n]
      "invalid"))

Inserts a zero into the returned vector where there was an error, in
this case [1 2 0 4]

  (kit/with-handler
    (vec (map int-half [2 4 5 8]))
    (kit/handle *number-error* [n]
      (kit/continue-with 0)))

Here's how to use an intermediate continue, like a CL restart.  The
with-handler form below will return [1 2 :oops 5 4]

  (defn int-half-vec [s]
    (reduce (fn [v i]
              (kit/with-handler
                (conj v (int-half i))
                (kit/bind-continue instead-of-half [& instead-seq]
                  (apply conj v instead-seq))))
      [] s))

  (kit/with-handler
    (int-half-vec [2 4 5 8])
    (kit/handle *number-error* [n]
      (kit/continue instead-of-half :oops n)))

A couple questions, for anyone still reading...

Should continue-names be namespace qualified, and therefore require
pre-definition in some namespace, like this?

  (kit/defcontinue skip-thing "docstring")

Could add 'catch' for Java Exceptions and 'finally' support to
with-handler forms.

--Chouser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to