有什么方法可以使本地'defonce'? (Clojure)
Is there any method of making local 'defonce'? (Clojure)
假设我需要做一个简单的计数器,我希望每次调用这个函数时计数器都递增,但有一件不愉快的事情:定义的 'counter' 不是本地的,我可以很容易地改变它来自另一个 space 的值,它破坏了封装。
(defn next []
(defonce counter (atom 0))
(println @counter)
(reset! counter (inc @counter)))
很多人说,如果我放置 'private' 元标记,它就会正确。所以函数看起来像这样:
(defn next []
(defonce ^:private counter (atom 0))
(println @counter)
(reset! counter (inc @counter)))
但我仍然可以从另一个 space 访问 'counter'。
有什么办法可以实现这种封装还是只能在协议层面实现?
Private 效果很好,您不应该从其他命名空间访问
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}]
还有一些小问题:
- 在ns顶层定义
counter
,不在函数内部,两者效果一样,但顶层更清晰
- 将
reset!
更改为(swap! counter inc)
,它将是线程安全的
您应该如何编写 next
函数:
(def ^{:arglists '([])} next
(let [counter (atom 0)]
#(let [after (swap! counter inc)
before (dec after)]
(println before)
after)))
这与你问题中的相同,只是它是线程安全的并且完全封装了 counter
原子。
假设我需要做一个简单的计数器,我希望每次调用这个函数时计数器都递增,但有一件不愉快的事情:定义的 'counter' 不是本地的,我可以很容易地改变它来自另一个 space 的值,它破坏了封装。
(defn next []
(defonce counter (atom 0))
(println @counter)
(reset! counter (inc @counter)))
很多人说,如果我放置 'private' 元标记,它就会正确。所以函数看起来像这样:
(defn next []
(defonce ^:private counter (atom 0))
(println @counter)
(reset! counter (inc @counter)))
但我仍然可以从另一个 space 访问 'counter'。
有什么办法可以实现这种封装还是只能在协议层面实现?
Private 效果很好,您不应该从其他命名空间访问
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}]
还有一些小问题:
- 在ns顶层定义
counter
,不在函数内部,两者效果一样,但顶层更清晰 - 将
reset!
更改为(swap! counter inc)
,它将是线程安全的
您应该如何编写 next
函数:
(def ^{:arglists '([])} next
(let [counter (atom 0)]
#(let [after (swap! counter inc)
before (dec after)]
(println before)
after)))
这与你问题中的相同,只是它是线程安全的并且完全封装了 counter
原子。