2017-07-07 4 views
1

Disons que je dois faire un simple compteur et que je veux que le compteur soit incrémenté chaque fois que j'appelle cette fonction, mais voici une chose désagréable: le 'counter' défini n'est pas local et je peux facilement changer sa valeur un autre espace, qui casse l'encapsulation.Existe-t-il une méthode de définition locale? (Clojure)

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

Beaucoup disent, il sera correct si je place une balise META 'privée'. Donc, la fonction ressemblera à ceci:

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

Mais j'ai toujours accès à 'compteur' d'un autre espace.
Existe-t-il un moyen d'implémenter cette encapsulation ou seulement au niveau de l'accord?

+0

Êtes-vous sûr d'avoir accès à un atome privé d'un autre ns? – mishadoff

+0

@mishadoff [Oui.] (Https://github.com/bbatsov/clojure-style-guide/blob/cb0be3a21c234fbb5bd152e3d67ffbf104140077/README.md#access-private-var) –

Répondre

4

Voilà comment vous devez écrire votre next fonction:

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

C'est le même que celui de votre question, sauf qu'il est thread-safe et encapsule complètement l'atome counter.

+0

Parfaitement! Mais ... '{{: arglists '([])}' - à quoi cela sert-il? – errfrom

+0

@errfrom Ce n'est pas vraiment nécessaire; essayez '(clojure.repl/doc next)' avec et sans lui et vous pouvez décider si vous voulez l'inclure. –

1

œuvres privées bien, vous ne devriez pas avoir accès d'autres espace de noms

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}] 

également Quelques problèmes mineurs:

  1. définissent counter au niveau supérieur de ns, pas à l'intérieur de la fonction, à la fois avoir le même effet, mais le plus haut niveau est plus clair
  2. changer reset! à (swap! counter inc), il sera thread sûr
+0

Il y avait un petit malentendu: je veux que le compteur soit uniquement dans cette fonction sans accès depuis le niveau supérieur en tant que liaison locale 'let'. C'est possible? – errfrom

+0

@errfrom bien, je ne pense pas que vous pouvez cacher mutable local dans une fonction sans accès de l'extérieur. Utilisez defrecord ou deftype à la place (similaire à la création d'un objet Counter dans java) – mishadoff

+0

J'ai révisé mon code et décidé que je devrais mettre la partie dans un module séparé, où définir au niveau supérieur ne créerait pas de problèmes, donc fondamentalement le problème résolu. Merci =) – errfrom