如何将列表传递给clojure的` - >`宏?

epl*_*oko 1 macros list clojure apply

我正在尝试找到一种通过函数列表来线程化值的方法.

首先,我有一个通常的基于铃声的代码:

(defn make-handler [routes]
  (-> routes
      (wrap-json-body)
      (wrap-cors)
      ;; and so on
      ))
Run Code Online (Sandbox Code Playgroud)

但这不是最优的,因为我想编写一个测试来检查路由实际上是用wrap-cors包裹的.我决定将包装器解压缩成def.所以代码变成如下:

(def middleware
  (list ('wrap-json-body)
        ('wrap-cors)
        ;; and so on
        ))

(defn make-handler [routes]
  (-> routes middleware))
Run Code Online (Sandbox Code Playgroud)

这显然不起作用,并不应该因为->宏没有将列表作为第二个参数.所以我尝试使用该apply函数来解决这个问题:

(defn make-handler [routes]
  (apply -> routes middleware))
Run Code Online (Sandbox Code Playgroud)

最终拯救了:

CompilerException java.lang.RuntimeException:无法获取宏的值:#'clojure.core/ - >

所以问题就出现了:如何将一个值列表传递给->宏(或者说,任何其他宏),就像apply对函数一样?

gal*_*dre 6

这是一个XY问题.

重点->是使代码更容易阅读.但是如果一个人为了使用而编写一个新宏->(在代码中没有人会看到因为它只存在于宏扩展中),在我看来,这样做了很多工作而没有任何好处.而且,我认为它模糊了代码,而不是澄清代码.

因此,本着从不使用函数将执行的宏的精神,我建议以下两个等效的解决方案:

解决方案1

(reduce #(%2 %) routes middleware)
Run Code Online (Sandbox Code Playgroud)

解决方案2

((apply comp middleware) routes)
Run Code Online (Sandbox Code Playgroud)

更好的方式

通过将middleware函数列表的定义更改为函数的组合,可以轻松简化第二种解决方案:

(def middleware
    (comp wrap-json-body
          wrap-cors
          ;; and so on
          ))

(middleware routes)
Run Code Online (Sandbox Code Playgroud)

当我开始学习Clojure时,我经常遇到这种模式,以至于我的许多早期项目都有一个freduce核心定义:

(defn freduce
   "Given an initial input and a collection of functions (f1,..,fn),
   This is logically equivalent to ((comp fn ... f1) input)."
   [in fs]
   (reduce #(%2 %) in fs))
Run Code Online (Sandbox Code Playgroud)

这完全没必要,有些人可能更喜欢直接使用reduce更清晰.但是,如果您不喜欢#(%2 %)在应用程序代码中盯着,可以在您的语言中添加另一个实用程序单词.