2
votes

I have a question in Clojure about recur. If I have a let-statement inside the loop, can the recur call be applying to the let statement instead of the values of the loop? For example, in this scenario:

(defn someFunction [listA listB]
  ("do something here...." 
      [new-listA new-listB]))

(defn anotherFunction [listA listB]
  ("do something here...." 
      [new-listA new-listB]))

(defn myFunction [firstList secondList]
  (loop [list1 (someMutation firstList)
         list2 (someMutation secondList)]
    (if (= "true" (someCondition))
      (let [[newlist1 newlist2]
           (someFunction list1 list2)] 
        (recur newlist1 newlist2))
      (anotherFunction list1 list2) )))

is (recur newlist1 newlist2) applying to the loop or to the let? And is there a way of skipping this let statement and calling recur directly with the two values returned by "someFunction", assuming that I can't change the fact that "someFunction" returns a vector with two arguments?

2

2 Answers

7
votes

recur always recurs to the closest loop or function, whichever is closer to the recur form. It does this by compiling to essentially a loop/jump statement that changes the values of the loop variables and then jumps back to the loop/fn point. This way it only uses one stack frame and can run full speed. This design has some intentional trade-offs:

  • you cannot interleave nested loops
  • recur can only be the last thing in an expression.
  • there must be the same number of args to recur as loop/the-function

This last one requires that you keep the let expression or something equivalent such as a destructuring form. Loops do allow destructuring of the arguments just like functions:

 (loop [[a b] [1 2]]
    (println a b)
    (if (< a 10)
      (recur [(inc a) (inc b)])))
1 2
2 3
3 4
4 5
5 6
6 7
7 8
8 9
9 10
10 11

So you could write your loop expression to take the result of someFunction directly

(loop [[list1 list2] [(someMutation firstList) (someMutation secondList)]]
  ...
  (recur (someFunction ...))
1
votes

let is not a valid recur target.

You might be able able to set things up in the let to destructure the vector and therefore skip the let as well.

Also, an equoted true is a little odd. Is that under your control? If so, should just look for the boolean true and then can just say (if (somecondition)...