The following piece of code does not differ very much from yours:
(defmacro execute [& forms]
`(redis/wcar {} ~@forms))
(defn get1 []
(redis/get 1))
(execute (get1) (get1) ...)
This is basically how carmine should be used (following this suggestion in the README). If it doesn't suit your needs, could you clarify why?
After the question was edited to present more clearly what should be accomplished I think I have a solution for you. You're trying to create a list of statements to be executed at some specific time in the future. To achieve this you're wrapping each statement inside a function and I agree, that's a lot of boilerplate.
But unnecessary. You can get the same result by having a macro that automatically creates thunks for you:
(defmacro statements [& forms]
`(vector
~@(for [f forms]
`(fn [] ~f))))
Now, whatever you pass to this macro will result in a vector of zero-parameter functions that can be evaluated e.g. using:
(defn execute-statements [fns]
(redis/wcar {} (mapv #(%) fns))
And your example turns into something along the lines of:
(defn munge-element [[a b c]]
(statements
(mapcat #(redis/hincrby a :whatever %) b)
(redis/sadd c b)
(redis/hsetnx a c))
(defn flush-queue! [queue_]
(mapv execute-statements queue_)
[])
Edit: This executes every batch in its own pipeline. If you want to do it in a single one, use concat
instead of conj
to build up your queue (in receive
) and (execute-statements queue_)
instead of (mapv execute-statements queue_)
.
Note: IIRC correctly, this:
(redis/wcar {} a [b c]])
returns the same result as this:
(redis/wcar {} a b c)
I.e., carmine collects the results somewhere and always returns a vector for all of them. Even if not, I think you can still avoid your dreaded boilerplate by just tweaking the stuff presented here a little bit.