Clojure宏:什么时候函数不能复制宏的行为?

MFa*_*ave 3 macros clojure

我正在玩clojure宏,我发现很多宏行为我可以用函数组合进行复制.

一个很好的例子是线程宏:

(defn add1 [n] (+ n 1))
(defn mult10 [n] (* n 10))

(defn threadline [arg]
  (-> arg
      add1
      mult10))
Run Code Online (Sandbox Code Playgroud)

我可以使用像管道这样的更高阶函数轻松复制这个:

(defn pipe [& fns]
  (reduce (fn [f g] (fn [arg] (g(f arg)))) fns))

(def pipeline
  (pipe
   #(+ % 1)
   #(* % 10)))
Run Code Online (Sandbox Code Playgroud)

必须存在无法用函数替换宏的情况.我想知道是否有人有这些情况的好例子,以及所涉及的重复主题.

Tay*_*ood 8

宏的一个重要优点是它们能够在编译时转换代码而无需评估任何代码.宏在编译期间接收代码作为数据,但函数在运行时接收.宏允许您在某种意义上扩展编译器.

例如,Clojure andor实现为递归宏,扩展为嵌套if表单.这允许对and/ or的内部形式进行惰性求值,即如果第一种or形式是真实的,则将返回其值,并且不会评估其他形式.如果您将and/ or作为函数编写,则在检查它们之前将对其所有参数进行求值.

在您的pipe函数示例中,短路控制流不是问题,但pipe->简单地展开到嵌套表单相比,会增加相当大的运行时复杂性.尝试作为函数实现的更有趣的宏可能是some->.

我发现很多宏行为我可以用函数组合来复制

如果您的函数适合它,您当然可以用函数组合替换一个简单的线程宏comp,类似于其他函数语言中的"无点"样式:#(-> % inc str)在功能上等同于(comp str inc)#(str (inc %)).

通常建议在可能的情况下选择函数,即使在编写宏时,通常也可以将大部分"工作"放到函数中.