Search code examples
clojurecounterlocalprivateencapsulation

Is there any method of making local 'defonce'? (Clojure)


Let's say I need to make a simple counter and I want counter to be incremented each time I call this function, but here is one unpleasant thing: defined 'counter' is not local and I can easily change its value from another space, that breaks encapsulation.

(defn next []
  (defonce counter (atom 0))
  (println @counter)
  (reset! counter (inc @counter)))

Many say, it will be correct if I place 'private' meta tag. So function will look like this:

(defn next []
  (defonce ^:private counter (atom 0))
  (println @counter)
  (reset! counter (inc @counter)))

But I still have access to 'counter' from another space.
Is there any way to implement this encapsulation or it's only at the agreement level?


Solution

  • Here's how you should write your next function:

    (def ^{:arglists '([])} next
      (let [counter (atom 0)]
        #(let [after (swap! counter inc)
               before (dec after)]
           (println before)
           after)))
    

    This is the same as the one in your question, except that it is thread-safe and completely encapsulates the counter atom.