Jiacai Liu
Jiacai Liu

Reputation: 2743

how to make correct fn args when create fn using macro

My Clojure app needs some handlers to do business, those handlers will preform some common parameters check, so I use a macro to do this like below:

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

Then I can use above macro like this:

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

Everything is fine this way.

As you can see, the params of generated fn is constructed directly from args of the marco, exception raised when params of generated fn can't constructed directly.

Take a example:

The params of the macro defapihandler now became like this:

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

In the macro, I want to build the param of the generated fn dynamicly like this:

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

The structure of passed-param looks like this: {:username "foo" :password "bar"}

Now I want to construct the variables used in body block in let block, Then following exception is thrown:

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

macroexpand create-user got this:

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

I suspect this exception is related to dynamic var used in let destructuring form, if my suspect is right, then how to construct variables used in body block ?

Upvotes: 0

Views: 276

Answers (1)

weavejester
weavejester

Reputation: 575

You need to pull the clause that builds your params-key vector out of the generated code.

So:

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

Or if you don't need 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))))

Upvotes: 3

Related Questions