Welcome! Please see the About page for a little more info on how this works.

+1 vote
in Sequences by

I see the following pattern in several places of clojure.core:

(if (next more)
  (recur ... (next more))
  ...)

where next is invoked twice before recur.

Is it “by design” or just unobserved code?

I expect a little more performant implementation like:

(if-let [next' (next more)]
  (recur ... next')
  ...)

3 Answers

+1 vote
by

It might be to avoid a risk of "holding the head" of the sequence. In general, you want the already-consumed part of the sequence to be garbage-collectible before the recursive call.

+1 vote
by
edited by

Could it be the order in which functions are defined in core?

Skimming through core.clj the if-let macro is actually defined at line 1841:

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L1841

while the "next more recur next more" fragments (in math stuff) are used before let or if-let exists (line 1055):

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L1055

Code after defmacro if-let is actually using the pattern you expect (line 3690):

https://github.com/clojure/clojure/blob/653b8465845a78ef7543e0a250078eea2d56b659/src/clj/clojure/core.clj#L3690

I think there is the regular single pass compilation with core.clj as with regular clojure code. Functions have to be defined before you can use them, both macros and defns.

I am not sure why the order have to be like this, maybe something with bootstrapping.

ago by
But `let` is a special form and has that `:special-form true` so maybe it is for performance/gc reasons after all, as pointed out by the other answers.
0 votes
by

I would recommend trying some performance benchmarks with the current vs. modified version of functions that you are thinking of, and seeing whether it leads to faster code or not, using the Criterium library to do performance measurements on Clojure/Java: https://github.com/hugoduncan/criterium

I have not done such measurements myself, but the HotSpot JIT compilers can do pretty amazing things sometimes.

by
i agree. performance is tricky.

you may also be interested in this somewhat similar topic:
https://ask.clojure.org/index.php/8361/changing-some?show=8361#q8361
by
surely I've tested it before asking.

  (= 1 1 1 1 1 1)

is 15% faster on my PC.
single `(next more)` is about 10 ns.

But anyway the question is not axactly about performance.
Its about useless repetition of already accomplished computation in the algorithm.
...