【问题标题】:how to make correct fn args when create fn using macro使用宏创建 fn 时如何制作正确的 fn args
【发布时间】:2016-12-24 11:46:44
【问题描述】:

我的 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 的参数是直接从宏的 args 构造的,当生成的 fn 的参数不能直接构造时会引发异常。

举个例子:

defapihandlerparams现在变成了这样:

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

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

(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"}

现在我想在let块中构造body块中使用的变量,然后抛出以下异常:

Caused by java.lang.IllegalArgumentException
Don't know how to create ISeq from: clojure.lang.Symbol

macroexpandcreate-user 得到这个:

(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))))

我怀疑这个异常与let解构形式中使用的动态var有关,如果我的怀疑是正确的,那么如何构造body块中使用的变量?

【问题讨论】:

    标签: clojure macros


    【解决方案1】:

    您需要从生成的代码中提取用于构建 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))))
    

    【讨论】:

    • 您的代码确实有效,但我仍然很困惑为什么要从字面上使用[],我的代码将尝试将param-keys# 解析为Symbol 而不是ISeq,正如例外所说
    • 参数向量中或let 子句左侧的符号未解析。例如,(let [x 'a, x 2] x)。在此,x 将首先设置为符号a,然后x 将设置为2。你不会发现xaa2
    猜你喜欢
    • 2010-09-23
    • 2012-07-17
    • 2019-12-30
    • 2017-08-04
    • 1970-01-01
    • 2016-12-13
    • 1970-01-01
    相关资源
    最近更新 更多