macros - 如何向类 defn 的 Clojure 宏添加文档字符串支持?

标签 macros clojure

我编写了一个宏来将函数定义包装在一些有用的日志记录中:

(defmacro defn-logged
  "Wraps functions in logging of input and output"
  [fn-name args & body]
  `(defn ~fn-name ~args
     (log/info '~fn-name "input:" ~@args)
     (let [return# (do ~@body)]
       (log/info '~fn-name "output:" return#)
       return#)))

这对于没有文档字符串的函数非常有效:

(defn-logged foo
  [x]
  (* 2 x))

(foo 3)
; INFO - foo input: 3
; INFO - foo output: 6
; 6

但是如果使用文档字符串的函数严重失败:

(defn-logged bar
  "bar doubles its input"
  [x]
  (* 2 x))
; IllegalArgumentException Parameter declaration clojure.tools.logging/info should be a vector

如何让我的宏同时适用于有和没有文档字符串的函数?

最佳答案

一种方法是查看传递给 defn-logged 的参数。如果名称后的第一个是字符串,则将其用作 doc 字符串,否则将 doc 留空:

(defmacro defn-logged
  "Wraps functions in logging of input and output"
  [fn-name & stuff]
   (let [has-doc (string? (first stuff))
         doc-string (if has-doc (first stuff))
         [args & body] (if has-doc (rest stuff) stuff)]
     `(defn ~fn-name {:doc ~doc-string} ~args
        (println '~fn-name "input:" ~@args)
        (let [return# (do ~@body)]
          (println '~fn-name "output:" return#)
          return#))))

用文档字符串测试:

(defn-logged my-plus "My plus documented" [x y] (+ x y))

(doc my-plus)
; -------------------------
; user/my-plus
; ([x y])
;   My plus documented
; nil

(my-plus 2 3)
; my-plus input: 2 3
; my-plus output: 5
; 5

没有文档字符串的测试:

(defn-logged my-mult [x y] (* x y))

(doc my-mult)
; -------------------------
; user/my-mult
; ([x y])
;   nil
; nil

(my-plus 2 3)
; my-mult input: 2 3
; my-mult output: 6
; 6

它仍然不是 defn 的完全等价物,至少因为 defn 支持在映射、阅读器宏和字符串中传递的元数据。但它适用于文档字符串。

关于macros - 如何向类 defn 的 Clojure 宏添加文档字符串支持?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24897818/

相关文章:

clojure - 棘手的 ClojureScript 语法

clojure - (gensym) 总是唯一的,`(symb#) 不是——为什么?

haskell - 如何比较两个函数的等价性,如 (λx.2*x) == (λx.x+x)?

c++ - 其创建被包装在宏中的对象的声明

c - 三元级联宏

scala - 从 Scala 宏更改编译器的类路径?

macros - 在Scheme中使用define-macro捕获变量的机制

用于重复使用 std::xyz 语句的 C++ 宏

list - 在 Clojure 中访问列表中的 map

transactions - 在 Clojure 中计算中止的事务