Clojure宏从函数构建更高阶函数

时间:2012-06-11 19:50:59

标签: macros clojure

我正在尝试编写一个构建类似于compojure中使用的中间件的宏。

我希望能够致电:

(def-middleware plus-two [x]
  (+ 2 x))

结果如下:

(defn plus-two [f]
 (fn [x]
   (f (+ 2 x)))

我已经远远没有阅读在线指南,但这对我没有用处:

(defmacro def-middleware [fn-name args & body]
  '(defn ~fn-name [f]
     (fn ~args
       (f ~@body))))

任何帮助或指向更好的宏写作指南的指针都会很棒,谢谢。

2 个答案:

答案 0 :(得分:8)

让我们看看macroexpand-1给我们的是什么:

user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x))))
(defn
~fn-name
[f]
$
(fn ~args$ (f (clojure.core/unquote-splicing body))))

不完全是我们追求的!首先要做的事情是:如果你想在宏中取消引用,你需要使用“`”(quasiquote / syntax-quote运算符),而不是“'”。此外,我不确定你用这些美元符号后会发生什么,但可能是你正在寻找使用gensym卫生的clojure方便快捷方式。但是您在使用它的每个标识符后立即需要它(所以[f#],而不是[f]$),并且每次出现标识符时都需要它。并且需要args。把所有这些放在一起:

user=> (defmacro def-middleware [fn-name args & body] `(defn ~fn-name [f#] (fn ~args (f# ~@body))))
#'user/def-middleware
user=> (clojure.pprint/pprint (macroexpand-1 '(def-middleware plus-two [x] (+ 2 x))))
(clojure.core/defn
 plus-two
 [f__594__auto__]
 (clojure.core/fn [x] (f__594__auto__ (+ 2 x))))
nil
user=> 

答案 1 :(得分:3)

也许这只是一个宏观练习,但定义中间件功能的具体例子并不好 - 你失去了中间件概念给你的很多灵活性。例如,为什么要评估(f (+ 2 x))而不是(+ 2 (f x))?两者都是包装此函数的合理方法,并且您的语法无法描述后者。真正只是定义一个函数返回函数是灵活,简单和容易的;这个宏给桌子带来了很少的东西。