Reputation: 253
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?
Upvotes: 1
Views: 397
Reputation: 13334
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.
Upvotes: 4
Reputation: 10789
Private works well, you shouldn't have access from other namespace
user> (ns a)
nil
a> (defonce ^:private counter (atom 0))
#'a/counter
a> (defn next []
(println @counter)
(swap! counter inc))
#'a/next
a> (next)
0
1
a> (next)
1
2
a> (next)
2
3
a> (next)
3
4
a> (ns b)
nil
b> (require 'a)
nil
b> (a/next)
4
5
b> (a/next)
5
6
b> a/counter
CompilerException java.lang.IllegalStateException: var: a/counter is not public
b> (ns a)
nil
a> a/counter
#object[clojure.lang.Atom 0x1ec64eb6 {:status :ready, :val 6}]
Also some minor issues:
counter
at the top level of ns, not inside the function, both have the same effect, but top level is clearerreset!
to (swap! counter inc)
, it will be thread safeUpvotes: 1