2017-01-12 8 views
0

У меня возникли проблемы с написанием макросов, которые используют деструктивные аргументы. Вот пример:Прохождение деструктурированных аргументов через макросы

(defmacro defny 
    [n args & forms] 
    `(defn ~n ~args [email protected])) 

(defmacro defnz 
    [n f args & forms] 
    `(defn ~n ~args 
    (do 
     (~f [email protected]) 
     [email protected]))) 

(defny y 
    [{:keys [value] :as args}] 
    (println "Y ARGS" args) 
    (println "Y VALUE" value)) 

(defnz z y 
    [{:keys [value] :as args}] 
    (println "Z ARGS" args) 
    (println "Z VALUE" value)) 

Вот, у меня есть два макроса, defny который просто вызывает до defn и defnz, который делает то же самое, но дополнительно принимает другую функцию, которая вызывающую до тела функции с арг defnz в ,

Когда я призываю г, я ожидаю увидеть оба значения и арг распечатанного то же самое, но вместо этого я получаю:

(z {:value 1}) 
Y ARGS {:keys [1], :as {:value 1}} 
Y VALUE nil 
Z ARGS {:value 1} 
Z VALUE 1 
=> nil 

Я могу понять, почему это происходит, деструктурированного арг {: клавиши [1 ]: как {: значение 1}} передаются y, но я не уверен, как исправить макрос defnz, чтобы деструктурированные аргументы могли быть переданы правильно.

+0

Попробуйте 'macroexpand' и' MACROEXPAND-1', чтобы увидеть, какой код ваши макросы производят. –

ответ

2

вы можете легко увидеть ошибку с macroexpansion:

(defnz z y 
    [{:keys [value] :as args}] 
    (println "Z ARGS" args) 
    (println "Z VALUE" value)) 

расширяется:

(defn z [{:keys [value], :as args}] 
    (do 
    (y [{:keys [value], :as args}]) 
    (println "Z ARGS" args) 
    (println "Z VALUE" value))) 

вы правильно: вы проходите все арги формируют у, но то, что вам нужно это сделать, что линии, которая должна быть расширена до (y args), где args - просто простой символ. Для того, чтобы сделать это в макрос (который превращает символы не пространства имен квалифицированных из них), вы должны использовать «цитата-Unquote» трюк:

(defmacro defnz 
    [n f args & forms] 
    `(defn ~n ~args 
    (do 
     (~f ~'args) 
     [email protected]))) 

теперь расширение будет правильно:

(defn z [{:keys [value], :as args}] 
    (do (y args) (println "Z ARGS" args) (println "Z VALUE" value))) 

но это довольно плохо Идея, поскольку вы точно не знаете, какое имя allargs будет передано в defnz, например {: keys [value]: as all-of-them} завершится с ошибкой (потому что defnz ожидает, что он будет args. Вы можете исправить его динамически получение имени allargs в defnz:

(defmacro defnz 
    [n f [a :as args] & forms] 
    (let [a (if (:as a) a (assoc a :as 'everything))] 
    `(defn ~n ~[a] 
     (do 
     (~f ~(:as a)) 
     [email protected])))) 

так что теперь она будет расширяться на следующее:

(defnz z y 
    [{:keys [value] :as args}] 
    (println "Z ARGS" args) 
    (println "Z VALUE" value)) 

;;(defn z [{:keys [value], :as args}] 
;; (do (y args) (println "Z ARGS" args) (println "Z VALUE" value))) 


(defnz z y 
    [{:keys [value] :as all-args}] 
    (println "Z ARGS" all-args) 
    (println "Z VALUE" value)) 

;;(defn z [{:keys [value], :as all-args}] 
;; (do 
;; (y all-args) 
;; (println "Z ARGS" all-args) 
;; (println "Z VALUE" value))) 

(defnz z y 
    [{:keys [value]}] 
    (println "Z ARGS" everything) 
    (println "Z VALUE" value)) 

;;(defn z [{:keys [value], :as everything}] 
;; (do 
;; (y everything) 
;; (println "Z ARGS" everything) 
;; (println "Z VALUE" value))) 
Смежные вопросы