Clojure宏从宏调用函数



我有一个ns,里面有一个宏。我要处理的烦人的事情是,taoensso.timbre宏只能作为一个可变表达式(timbre/info "a" "b" "c")。项目列表不会正确登录(timbre/info ["a" "b" "c"])。我试图创建一个包装器宏,让代码以相同的可变形式调用(logger/info),然后处理所有元素,然后传递给timbre/info

(ns logger
(:require [taoensso.timbre :as timbre :include-macros true])) ; a third party logger
;; A bit of pseudo code here. If you pass in a vector of args, you should get a vector of args with some changes
(defn scrub [args]
(if (listy)
(mapv (fn [a] (scrub args) args)
(if (is-entry a) {:a "xxx"} a)
(defmacro info
[& args]
`(timbre/info ~@(scrub args)))

这不起作用,因为screw会立即调用,并且不会解析传入的符号。我需要像这两个一样不起作用的东西。

(defmacro info
[& args]
`(timbre/info @(scrub-log-pii ~args)))
(defmacro info
[& args]
`(timbre/info ~@('scrub-log-pii args)))

我最后的想法是尝试将音色宏封装在一个函数中,以便宏和评估按正确的顺序进行。然而,没有办法";应用";到宏。

(defn info3
[& args]
(timbre/info (scrub-log-pii (vec args))))

有什么想法吗?

这并不是对所说问题(宏应用程序内容(的确切回答,而是实际的timbre解决方案,可能适用于您的特定情况:

在这里,您可以看到所有的音色宏都使用log!宏,后者反过来接受args的集合。

所以,只需将您的程序作为来执行

(defmacro info* [args] `(log! :info :p ~args ~{:?line (fline &form)}))

应该做到这一点。

您遇到了一个使用称为"乌龟一路往下走";。也就是说,您可能需要编写一个包装器宏,然后再编写另一个包装程序宏,而不是使用函数组合,等等。

编写宏的详细步骤如下所述:

  • 如何编写Clojure线程宏

对于您的特定问题,我们可以这样做:

(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.pprint :as pp]))
(defn infix-impl
[a op b]
(list op a b))
(defmacro infix
"Allows user to have Java-style infix operators:
(infix 2 + 3)
"
[a op b] (infix-impl a op b))
(defn infix-seq-impl
[args]
`(let [form#   (cons 'infix ~args)
result# (eval form#)]
result#))
(defmacro infix-seq
[args] (infix-seq-impl args))
(dotest
(is= 5 (infix 2 + 3))
(let [params '[2 + 3]]
(pp/pprint (infix-seq-impl 'params))
(is= 5 (infix-seq params))))

在这里,我们使用infix宏来展示如何创建一个接受参数序列而不是N个标量参数的包装器宏infix-seq。打印输出显示生成的代码:

(clojure.core/let
[form__24889__auto__    (clojure.core/cons 'tst.demo.core/infix params)
result__24890__auto__  (clojure.core/eval form__24889__auto__)]
result__24890__auto__)

一个更通用的版本

下面的CCD_ 8宏允许您将宏的名称传递为"0";应用";到参数序列:


(defn applied-impl
[f args]
`(let [form#   (cons ~f ~args)
result# (eval form#)]
result#))
(defmacro applied 
[f args] (applied-impl f args))
(dotest
(nl)
(let [params '[2 + 3]]
;      (applied 'infix params)   ; using a single quote fails
(is= 5 (applied `infix params)) ; using a backquote works
(is= 5 (applied 'tst.demo.core/infix params)) ; can also use fully-qualified symbol with single-quote
(is= 5 (applied (quote tst.demo.core/infix) params)) ; single-quote is a "reader macro" for (quote ...)
))

最新更新