My first clojure macro

I'm finally experimenting with writing macros in clojure. Learning macros is (for me at least) a 4 stage process:

  1. Learn to use them (pretty straightforward)
  2. Learn to read their implementations (including the quoting)
  3. Learning to write them (in progress)
  4. Learning when to write them (in progress)

Those last two are iterative; #4 is especially tricky -- the web is full of general considerations ("when a function won't do", "when you want new syntax", "when you need to make decisions at compile time", etc) - but actually making that judgment in practice, takes... well practice.

Hence this exercise. Anyway to the code:

Clojure offers the if-let and when-let macros that allow you to combine a let block with testing the binding for nil:

(when-let [a (some-fn)]  
   (do-something-with a))

(if-let [a (some-fn)]  
   (do-something-with a)  

I found myself (on some real code) wanting to be able to do something similar with try:

(try-let [a (some-potentially-exceptional-fn)]
  (do-something-with a))

(try-let [a (some-potentially-exceptional-fn)]
  (do-something-with a)
  ArithmeticException ((println (.getMessage e)) 42)
  :else (do-something-by-default-fn)
  :finally (println "always"))


So I wrote this (non-hygenic) macro that seems to do the job:

(defmacro try-let [let-expr good-expr & {fin-expr :finally else-expr :else :as handlers}]
  (letfn [(wrap [arg] (if (seq? arg) arg (list arg)))]
  `(try (let ~let-expr ~good-expr)
    ~@(map #(apply list 'catch (key %) 'e (wrap (val %))) (dissoc handlers :finally :else))
    ~(if else-expr `(catch Exception ~'e ~else-expr))
    (finally ~(if fin-expr `~fin-expr ())))))

Thing is... I don't if it's a good idea or not. For one thing its not hygienic (it implicitly declares e that can be used in the handler clauses) though this seems the kind of case that sort of thing is for.

For another... I don't know if its correct. It seems to be (I've tested all the scenarios I can think of), but this is kinda like security -- I suspect anyone can write a macro that they themselves can't break, but that doesn't mean its correct.

Some things to note: - e is available to handler expressions
- the local function wrap allows for a complex expression or single value to be spliced in
- any number of handlers can be included
- ':else' (default) handler and ':finally' handlers are optional (as are any others!)

In short: I'm interested in any opinions/feedback that aim at learning steps 3 & 4 (writing and when to write). Fire away!