I'm not very familiar with Clojure/Lisp macros. I would like to write apply-recur
macro which would have same meaning as (apply recur ...)
I guess there is no real need for such macro but I think it's a good exercise. So I'm asking for your solution.
Well, there really is no need for that, if only because recur
cannot take varargs (a recur
to the top of the function takes a single final seqable argument grouping all arguments pass the last required argument). This doesn't affect the validity of the exercise, of course.
However, there is a problem in that a "proper" apply-recur
should presumably handle argument seqs returned by arbitrary expressions and not only literals:
;; this should work...
(apply-recur [1 2 3])
;; ...and this should have the same effect...
(apply-recur (vector 1 2 3))
;; ...as should this, if (foo) returns [1 2 3]
(apply-recur (foo))
However, the value of an arbitrary expression such as (foo)
is simply not available, in general, at macro expansion time. (Perhaps (vector 1 2 3)
might be assumed to always yield the same value, but foo
might mean different things at different times (one reason eval
wouldn't work), be a let
-bound local rather than a Var (another reason eval
wouldn't work) etc.)
Thus to write a fully general apply-recur
, we would need to be able to determine how many arguments a regular recur
form would expect and have (apply-recur some-expression)
expand to something like
(let [seval# some-expression]
(recur (nth seval# 0)
(nth seval# 1)
...
(nth seval# n-1))) ; n-1 being the number of the final parameter
(The final nth
might need to be nthnext
if we're dealing with varargs, which presents a problem similar to what is described in the next paragraph. Also, it would be a good idea to add an assertion to check the length of the seqable returned by some-expression
.)
I am not aware of any method to determine the proper arity of a recur
at a particular spot in the code at macro-expansion time. That does not mean one isn't available -- that's something the compiler needs to know anyway, so perhaps there is a way to extract that information from its internals. Even so, any method for doing that would almost certainly need to rely on implementation details which might change in the future.
Thus the conclusion is this: even if it is at all possible to write such a macro (which might not even be the case), it is likely that any implementation would be very fragile.
As a final remark, writing an apply-recur
which would only be capable of dealing with literals (actually the general structure of the arg seq would need to be given as a literal; the arguments themselves -- not necessarily, so this could work: (apply-recur [foo bar baz])
=> (recur foo bar baz)
) would be fairly simple. I'm not spoiling the exercise by giving away the solution, but, as a hint, consider using ~@
.
apply
is a function that takes another function as an argument. recur
is a special form, not a function, so it cannot be passed to apply
.