I am trying to write a macro that will allow me to do the following
(without-nesting
(:= x 1)
(:= y 2)
(:= z 3)
(db-call x y z)
(:= p 33)
(db-call x y z p))
becomes
(let [x 1
y 2
z 3]
(db-call x y z)
(let [p 33]
(db-call x y z p)))
So far my implementation has been the following
(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)))))
I'm having issues when remaining
is going to be empty. In my current implementation a nil
gets placed which prevents the last form in non-bindings
to return its value. I have no clue on how to proceed with a recursive macro. Can someone help
UPDATE:
So I was able to get rid of the nil but I just want to know if there's a better way to deal with this
(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)]
[]))))
Also would this be a good way to write code? Or do you see any caveats? For me it looks more linear as compared to nested let
I do see how ppl may abuse this. In the case of let
, if the nesting becomes too much, then it's a hint to refactor the code. This might hide that
Just use let
. It is already recursive. To incorporate function calls where you only care about the side effects, the convention is to bind to an underscore.
(let [x 1
y 2
z 3
_ (db-call x y z)
p 33
_ (db-call x y z p)])