Search code examples
recursionclojurecompiler-errorssequence

Why doesn't this recur within a mapcat terminate?


Why doesn't this recur within mapcat terminate, whereas the named fn call version equivalent does?

(def tree [1 [2 [3 5]] [6 [[1 2] [8 [9 10]]]]])

(defn leaves
  [node]
  (mapcat #(if (vector? %) (leaves %) [%]) node))

(leaves tree)
;; => (1 2 3 5 6 1 2 8 9 10)

(defn leaves-with-recur
  [node]
  (mapcat #(if (vector? %) (recur %) [%]) node))

(leaves-with-recur tree)
;; Never terminates

If such use of recur is a straight-forward no-no, is there any reason why the Clojure compiler should not catch this scenario and warn the programmer, or refuse to compile it even? As is the case for non-tail position invocations of recur for instance.


Solution

  • #(if (vector? %) (recur %) [%])
    

    is a shorthand for

    (fn [%]
      (if (vector? %)
        (recur %)
        [%]))
    

    That recur will recurse to this anonymous function, not to any outer one, and since it doesn't change anything, this is an endless loop.

    As for warning—the halting problem is known to be unsolvable, and there is a risk of falling into an endless loop at compile time even when just trying to put some heuristics into place.