使用宏创建fn时如何制作正确的fn args

时间:2016-12-24 11:46:44

标签: clojure macros

我的Clojure应用程序需要一些处理程序来做生意,那些处理程序会执行一些常见的参数检查,所以我使用宏来执行此操作,如下所示:

(defmacro defapihandler [handler-name params & body]
  `(defn ~handler-name ~params
     (let [keyed-params# (map keyword '~params)
           checked-ret# (check-param (zipmap keyed-params# ~params))]
       (if (:is-ok checked-ret#)
         (do ~@body)
         (-> (response {:code 10000
                        :msg  (format " %s are missing !!!" (:missed-params checked-ret#))})
             (status 400))))))

然后我可以像这样使用上面的宏:

(defapihandler create-user [username password birthday]
  ;; todo 
)

一切都很好。

正如你所看到的,生成的fn的参数是直接从marco的args构造的,当生成的fn的params不能直接构造时会引发异常。

举个例子:

params的{​​{1}}现在变成了这样:

defapihandler

在宏中,我想像这样动态地构建生成的fn的参数:

[{:key :username :checker [not-nil?]} {:key :password :checkers [is-secure?]} ...]

(defmacro defapihandler [handler-name params & body] `(defn ~handler-name [passed-param#] (let [param-keys# (vec (map (comp symbol name :key) ~params)) {:keys param-keys#} passed-param#] ;; some check (do ~@body)))) (defapihandler create-user [{:key :username :checkers []}] (println username)) 的结构如下所示:passed-param

现在我想在{:username "foo" :password "bar"}块中构造body块中使用的变量,然后抛出以下异常:

let

Caused by java.lang.IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol macroexpand得到了这个:

create-user

我怀疑这个异常与let destructuring表单中使用的动态var有关,如果我的怀疑是正确的,那么如何构造(defn create-user [passed-param__10243__auto__] (let [param-keys__10244__auto__ (vec (map (comp symbol name :key) [{:key :username, :checkers []}])) {:keys param-keys__10244__auto__} passed-param__10243__auto__] (do (println username)))) 块中使用的变量?

1 个答案:

答案 0 :(得分:3)

您需要从生成的代码中提取构建params-key向量的子句。

所以:

(defmacro defapihandler [handler-name params & body]
  (let [param-keys (map (comp symbol name :key) params)]
    `(defn ~handler-name [passed-param#]
       (let [{:keys [~@param-keys]} passed-param#]
         ;; some check
         (do ~@body)))))

或者,如果您不需要passed-param#

(defmacro defapihandler [handler-name params & body]
  (let [param-keys (map (comp symbol name :key) params)]
    `(defn ~handler-name [{:keys [~@param-keys]}]
       ;; some check
       (do ~@body))))