Clojure 宏允许在没有 let 绑定的情况下编写代码

Clojure macro to allow writing code without let bindings

我正在尝试编写一个允许我执行以下操作的宏

(without-nesting
  (:= x 1)
  (:= y 2)
  (:= z 3)
  (db-call x y z)
  (:= p 33)
  (db-call x y z p))

变成

(let [x 1 
      y 2 
      z 3]
  (db-call x y z)
  (let [p 33]
    (db-call x y z p)))  

到目前为止,我的实现如下

(defn assignment?
  [[s _]]
  (= s ':=))

(defmacro without-nesting
  [& body]
  (let [[bindings xs] (split-with assignment? body)
        [non-bindings remaining] (split-with (complement assignment?) xs)]
    `(let [~@(mapcat rest bindings)]
       ~@non-bindings
       ~(when (seq remaining)
          `(without-nesting ~@remaining)))))

remaining 为空时,我遇到了问题。在我当前的实现中,放置了一个 nil 以防止 non-bindings 中的最后一个表单变为 return 其值。我不知道如何处理递归宏。有人可以帮忙

更新:

所以我能够摆脱 nil 但我只想知道是否有更好的方法来处理这个问题

(defmacro without-nesting
  [& body]
  (let [[bindings xs] (split-with assignment? body)
        [non-bindings remaining] (split-with (complement assignment?) xs)]
    `(let [~@(mapcat rest bindings)]
       ~@non-bindings
       ~@(if (seq remaining)
           [`(without-nesting ~@remaining)]
           []))))  

这也是编写代码的好方法吗?或者你看到任何警告?对我来说,与嵌套 let

相比,它看起来更线性

我确实知道人们会如何滥用它。在let的情况下,如果嵌套变得过多,则提示重构代码。这可能会隐藏

只需使用let。它已经是递归的。要在您只关心副作用的地方合并函数调用,约定是绑定到下划线。

(let [x 1 
      y 2 
      z 3
      _ (db-call x y z)
      p 33
      _ (db-call x y z p)])