Does the order of function calls inside a Clojure map guarantee runtime order processing?

Viewed 90

I'm working on the following function:

(defn process-request [request]
  (if (not (db/empty?))
    {:person (db/get-person!) :violations (:violations (process-violations request))}
    {:person {} :violations ["no-person"]}))

I need the process-violations function to only be called if the db is not empty. That's why it's in the if block.

However, I also need it to be called before the db/get-person! function. Otherwise, the value will be outdated.

So I wonder:

  • How would I do that?
  • And anyway, is the order of values in a map enough to guarantee that a function will always be called before another?
2 Answers

No, maps are not ordered. For small literal maps it will probably work, but you should not rely on it, and for larger maps it indeed does fail:

user=> {0 (println 0) 1 (println 1) 2 (println 2) 3 (println 3) 4 (println 4) 5 (println 5) 6 (println 6) 7 (println 7) 8 (println 8) 9 (println 9)}
0
7
1
4
6
3
2
9
5
8
{0 nil, 7 nil, 1 nil, 4 nil, 6 nil, 3 nil, 2 nil, 9 nil, 5 nil, 8 nil}

But it is always easy to force ordering by using let bindings, which are evalauted, in order, before the let body.

(let [person (db/get-person!)]
  {:person person :violations ...})

The reader will read the map in the code into a map, about which you should make no ordering assumptions, so do not rely on the evaluation order of those values. (In reality, it's an array map, and it probably is evaluated in the same order as defined lexically, but you should not rely on that.)

(defn process-request [request]
  (if (not (db/empty?))
    (let [violations (:violations (process-violations request))
          person (db/get-person!)]
      {:person person :violations violations})
    {:person {} :violations ["no-person"]}))

would be safer.

Related