What I'm trying to do:
Create a macro that can take a vector of vectors (exception handling logic, called handlers in my examples at the bottom), some some other data (exception prone body, called body in my examples at the bottom), and generate slingshot try/catch logic.
e.g. I want to turn
(cp
;; vector of vectors (exception handling logic)
[[Exception println]]
;;the "other data" exception prone body
(throw+ (ex-info :crash "and burn")))
into
(try+
(throw+ (ex-info :crash "and burn"))
(catch Exception e (println e)))
I want to do this because I believe the normal try/catch syntax is always verbose, especially so when catching multiple errors.
I'm able to get pretty close but I can't figure out how to properly evaluate the symbols within a macro to get what I want. I believe my example 2 below is the most interesting.
My attempts so far:
1) macro that returns appropriate data as a list, but I don't want to return it I want to evaluate it. Calling eval
instead of pprint
on the result gives
ClassCastException java.lang.Class cannot be cast to clojure.lang.IFn stream-stocks.core/eval27882 (form-init2616933651136754630.clj:1)
.
(defmacro cp "handle exceptions"
[handlers & body]
`(loop [h# ~handlers
acc# (conj '~body 'slingshot.slingshot/try+)]
(if h#
(recur (next h#)
(concat acc# (list (list 'catch (first (first h#)) 'e# (reverse (conj (next (first h#)) 'e#)))) ))
acc#)))
(let [handlers [[Exception println] [java.lang.NullPointerException type]
[:test-throw #(println "Works! Will handle exception: " %)]]]
(pprint (cp [[Exception println] [java.lang.NullPointerException type]
[:test-throw #(println "Works! Will handle exception: " %)]]
(slingshot.slingshot/throw+ {:test-throw "Test-throw error msg"})))
(pprint (cp handlers
(slingshot.slingshot/throw+ {:test-throw "Test-throw error msg"}))))
2) macro that works with hard coded data, but not symbols
The macro call that that Does Not work below gives error:
CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(/tmp/form-init2616933651136754630.clj:6:3)
.
(defmacro cp "handle exceptions"
[handlers2 & body]
(loop [h# handlers2
acc# (conj (list (first body)) 'slingshot.slingshot/try+)]
(if h#
(recur (next h#)
(concat acc# (list (list 'catch (first (first h#)) 'e# (reverse (conj (next (first h#)) 'e#))))))
acc#)))
(let [handlers [ [Exception println] [java.lang.NullPointerException type]
[:test-throw #(println "Works! Will handle exception: " %)]]]
;;I work
(cp [ [Exception println] [java.lang.NullPointerException type]
[:test-throw #(println "Works! Will handle exception: " %)]]
(slingshot.slingshot/throw+ {:test-throw "Test-throw error msg"}))
;;I do NOT work
(cp handlers
(slingshot.slingshot/throw+ {:test-throw "Test-throw error msg"})))
3) function that does actually work iff I quote the handlers and body, which I really want to avoid
(defn cpf "handle exceptions" [handlers & body]
(eval (loop [h handlers
acc (conj body 'slingshot.slingshot/try+)]
(if h
(recur (next h)
(concat acc (list (list 'catch (first (first h)) 'e (reverse (conj (next (first h)) 'e))))))
acc))))
(let [handlers [ '[Exception println] '[java.lang.NullPointerException type]
'[:test-throw #(println "Works! Will handle exception: " %)]
]]
(cpf [ '[Exception println]
'[:test-throw println]
]
'(println "Should get called")
'(throw+ {:test-throw "Test-throw error msg"})
'(println "Should not get called")
)
(cpf handlers
'(println "Should get called")
'(throw+ {:test-throw "Test-throw error msg"})
'(println "Should not get called")))
catch
block repetitions? Or something else? – leetwinski