【发布时间】:2023-03-19 05:45:01
【问题描述】:
我想做的事:
创建一个宏,它可以采用向量向量(异常处理逻辑,在我的底部示例中称为 handlers),一些其他数据(易发生异常的主体,称为 body 在底部的示例中),并生成弹弓 try/catch 逻辑。
例如我要转
(cp
;; vector of vectors (exception handling logic)
[[Exception println]]
;;the "other data" exception prone body
(throw+ (ex-info :crash "and burn")))
进入
(try+
(throw+ (ex-info :crash "and burn"))
(catch Exception e (println e)))
我想这样做是因为我相信正常的 try/catch 语法总是很冗长,尤其是在捕获多个错误时。
我可以非常接近,但我不知道如何正确评估宏中的符号以获得我想要的。我相信下面的示例 2 是最有趣的。
我目前的尝试:
1) 将适当数据作为列表返回的宏,但我不想返回它我想评估它。在结果上调用 eval 而不是 pprint 会给出
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) 适用于硬编码数据而非符号的宏
下面不工作的宏调用给出错误:
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) 确实有效的函数如果我引用了我真的想避免的处理程序和主体
(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块重复?还是别的什么? -
是的,我的目标是可读性。我认为将潜在的多个 catch 块组合成一些我可以在其他地方定义的变量。最好是包裹弹弓。
-
你不认为在异常发生的地方准确地捕捉异常比把这个逻辑移到别处更可读吗?为什么不在一个 catch 块中捕获任何异常,然后将其传递给某个处理函数,该函数知道如何处理每种异常类型?也许是多方法之类的。