Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 如何从源文件而不是REPL使用macroexpand-1_Clojure - Fatal编程技术网

Clojure 如何从源文件而不是REPL使用macroexpand-1

Clojure 如何从源文件而不是REPL使用macroexpand-1,clojure,Clojure,当不在REPL上工作时,使用macroexpand-1测试Clojure宏的正确方法是什么?假设我们要测试一个向任意值添加3的宏: (defmacro iiinc [x] `(+ 3 ~x)) 我通常更喜欢使用我最喜欢的文本编辑器/IDE来开发代码,并使用它来持续运行我的单元测试,而不是在REPL上工作。但是,当尝试使用macroexpand-1迭代开发新宏时,这不起作用 问题似乎是macroexpand-1和deftest宏之间存在冲突。因此,解决方案是避免在(deftest…表单中使用

当不在REPL上工作时,使用
macroexpand-1
测试Clojure宏的正确方法是什么?

假设我们要测试一个向任意值添加3的宏:

(defmacro iiinc [x]
  `(+ 3 ~x))
我通常更喜欢使用我最喜欢的文本编辑器/IDE来开发代码,并使用它来持续运行我的单元测试,而不是在REPL上工作。但是,当尝试使用
macroexpand-1
迭代开发新宏时,这不起作用

问题似乎是
macroexpand-1
deftest
宏之间存在冲突。因此,解决方案是避免在
(deftest…
表单中使用
macroexpand-1
。但是,它在
deftest
之外工作得很好,即使它仍然在单元测试源文件中。下面是一个例子:

; source file tst.clj.core

(newline)
(println "This works when not in (deftest ...)")
(println "(macroexpand-1 '(iiinc 2))  =>" (macroexpand-1 '(iiinc 2)))
    
(deftest t-stuff
  (newline)
  (println "But it is broken inside (deftest ...)")
  (println "(macroexpand-1 '(iiinc 2))  =>" (macroexpand-1 '(iiinc 2)))

  (newline)
  (println "However, we can use the macro itself fine in our tests")
  (println "  (iiinc 2) =>" (iiinc 2))
  (is (= 5 (iiinc 2))))  ; unit test works fine
上述结果如下:

This works when not in (deftest ...)
(macroexpand-1 '(iiinc 2))  => (clojure.core/+ 3 2)

Testing tst.clj.core

But it is broken inside (deftest ...)
(macroexpand-1 '(iiinc 2))  => (iiinc 2)

However, we can use the macro itself fine in our tests
  (iiinc 2) => 5

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

后记 请参阅下面的答案以了解更多信息 关于如何在Clojure中编写宏的更完整讨论:


问题在于
deftest
之外的表达式是在编译时运行的,而
*ns*
是绑定的,而
deftest
内部的表达式是稍后在运行时运行的,而
*ns*
是不绑定的

为什么这很重要?因为
macroexpand
需要解析当前名称空间中的符号
iinc
,以确定它是否是宏,并查找它的定义(如果是宏)以调用它。因此,您可以看到您的
macroexpand
在编译时工作,而不是在运行时

解决办法是什么?当然不要在编译时运行测试!相反,您应该正确地对表单进行名称空间限定,以便它们不依赖于
*ns*
的编译时便利性。你可以通过手写来完成

(deftest t-stuff
  (println "(macroexpand-1 '(my.ns/iiinc 2))  =>" (macroexpand-1 '(my.ns/iiinc 2)))))
但是,正确的解决方案是,在引用用于以后评估的表单时,就像编写宏时一样,执行应该始终执行的操作:使用语法引号,而不是常规引号。通过这种方式,编译器在编译时为您计算出所需的名称空间,并将其插入表单中,以便在运行时它仍然存在:

(deftest t-stuff
  (println "(macroexpand-1 `(iiinc 2))  =>" (macroexpand-1 `(iiinc 2)))))

这是一个很好的答案,但我认为其中的一些内容应该在问题本身,因为它解释了你试图做的不起作用的事情。如果没有这个答案的背景,没有人能回答你的简短问题。四年后回顾这一点,我现在认为整个答案应该转移到这个问题中。对于“如何使用macroexpand测试宏”的问题,您没有提供任何答案;你只是得出结论说它不起作用。这是一个非常彻底的问题,我认为我的回答提供了一个很好的答案,我只是不明白这是一个怎样的答案。