在其正文中获取函数的名称或:测试正文

时间:2017-04-16 17:28:08

标签: clojure

在clojure中,是否可以在其身体内部获取函数的名称,希望在不引入函数定义的新包装的情况下完成?还可以在函数:test属性的主体内部访问函数的名称吗?

为了激励,这对于某些日志记录情况很有帮助,也可以保持:test的主体不会更改它所提供的函数名称。

meta最接近的简短说明如下;据我所知,在{3}}中没有this概念提供给meta

(defn a [] (:name (meta (var a))))

显然,使用包装器宏很容易实现。

编辑:幸运的是到目前为止还没有人提到lambda组合器。

1 个答案:

答案 0 :(得分:2)

有两种方法可以解决您的问题。但是,我怀疑要完全自动化您想要做的事情,您需要定义自己的自定义defn替换/包装。

首先要意识到的是,所有功能都是匿名的。当我们输入:

(defn hello [] (println "hi"))
我们正在打字:

(def hello (fn [] (println "hi"))

我们正在创建一个符号hello,指向匿名var,后者又指向匿名函数。但是,我们可以给函数一个内部名称"像这样:

(def hello (fn fn-hello [] (println "hi")))

现在我们可以通过hello或从内部使用hellofn-hello符号从外部访问该功能(请不要使用{{1}在这两个地方或你造成很多混乱......即使它是合法的。)

我经常在(否则)匿名函数中使用hello方法,因为抛出的任何异常都将包含fn-hello符号,这使得追踪问题的根源变得更加容易(错误的行号)经常从堆栈跟踪中遗漏)。例如,当使用Instaparse时,我们需要一个匿名转换函数的映射,如:

fn-hello

并为每个功能提供"内部名称"使调试更容易,更容易。如果Clojure有更好的错误信息,这可能是不必要的,但这是一个长期(并且迄今尚未实现)的愿望。

您可以在此处找到更多详细信息:https://clojure.org/reference/special_forms#fn

如果你仔细阅读,它声称 { :identifier fn-identifier :string fn-string :integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)]) :boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)]) :namespace (fn fn-namespace [arg] [:namespace arg]) :prefix (fn fn-prefix [arg] [:prefix arg]) :organization (fn fn-organization [arg] [:organization arg]) :contact (fn fn-contact [arg] [:contact arg]) :description (fn fn-description [arg] [:description arg]) :presence (fn fn-presence [arg] [:presence arg]) :revision (fn fn-revision [& args] (prepend :revision args)) :iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)]) :reference (fn fn-reference [arg] [:reference arg]) :identity (fn fn-identity [& args] (prepend :identity args)) :typedef (fn fn-typedef [& args] (prepend :typedef args)) :container (fn fn-container [& args] (prepend :container args)) :rpc (fn fn-rpc [& args] (prepend :rpc args)) :input (fn fn-input [& args] (prepend :input args)) ...<snip>... } 扩展为

(defn foo [x] ...)

虽然您可能需要进行试验,看看这是否已经解决了您正在寻找的用例。它可以在这个示例中看到,我们明确地避免使用内部(def foo (fn foo [x] ...)) 名称:

fn-fact

此版本也有效:

(def fact (fn [x] ; fn-fact omitted here
            (if (zero? x) 
              1 
              (* x (fact (dec x))))))

(fact 4) => 24

所以我们看到&#34;内部名称&#34; (def fact (fn fn-fact [x] (if (zero? x) 1 (* x (fn-fact (dec x)))))) (fact 4) => 24 (fn-fact 4) => Unable to resolve symbol: fn-fact 隐藏在函数内部,从外部看不见。

第二种方法,如果使用宏,则使用fn-fact全局数据从源代码访问行号。 In the Tupelo library此技术用于改进

的错误消息
&form

这个方便的宏允许使用单位测试,如:

(defmacro dotest [& body] ; #todo README & tests
  (let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
    `(clojure.test/deftest ~test-name-sym ~@body)))

评估

(dotest
  (is (= 3 (inc 2))))

而不是手动输入

(deftest test-line-123   ; assuming this is on line 123 in source file
  (is (= 3 (inc 2))))

您可以访问任何宏中的(deftest t-addition (is (= 3 (inc 2)))) 和其他信息,这些信息可以使您的错误消息和/或异常信息对于试图调试问题的不良读者更有用。

除了上面的宏包装器示例之外,还有另一个(涉及更多)相同技术can be seen in the Plumatic Schema library的示例,它们用(:line (meta &form))包装扩展版本。

您可能还希望查看此问题,以澄清Clojure如何使用&#34;匿名&#34; var作为符号和函数之间的中介:When to use a Var instead of a function?