2012-11-27 2 views
5

Ho una situazione in cui creo e distruggo oggetti in uno spazio dei nomi clojure e voglio che un altro spazio dei nomi sia coordinato. Comunque non voglio che il primo namespace debba chiamare il secondo esplicitamente sulla distruzione dell'oggetto.Come agganciare in Clojure

In Java, potrei usare un listener. Sfortunatamente le librerie java sottostanti non segnalano eventi sulla distruzione degli oggetti. Se fossi in Emacs-Lisp, allora userei i ganci che fanno il trucco.

Ora, in clojure non ne sono così sicuro. Ho trovato la biblioteca di Robert Hooke https://github.com/technomancy/robert-hooke. Ma questo è più come defadvice in termini elis: sto componendo funzioni. Più oltre la documentazione dice:

"I ganci sono pensati per estendere le funzioni che non controlli, se possiedi la funzione obiettivo ci sono ovviamente modi migliori per cambiare il suo comportamento."

Purtroppo non lo trovo così ovvio.

Un'altra possibilità sarebbe utilizzare add-watch, ma questo è contrassegnato come alfa.

Mi manca un'altra soluzione ovvia?

Esempio Aggiunto:

Quindi, prima dello spazio dei nomi ....

(ns scratch-clj.first 
    (:require [scratch-clj.another])) 

(def listf (ref())) 

(defn add-object [] 
    (dosync 
    (ref-set listf (conj 
       @listf (Object.)))) 
    (println listf)) 


(defn remove-object [] 
    (scratch-clj.another/do-something-useful (first @listf)) 
    (dosync 
    (ref-set listf (rest @listf))) 
    (println listf)) 


(add-object) 
(remove-object) 

Seconda namespace

(ns scratch-clj.another) 


(defn do-something-useful [object] 
    (println "object removed is:" object)) 

Il problema qui è che graffi clj.first deve richiedere un'altra ed esplicitamente spingere gli eventi di rimozione attraverso. Questo è un po 'goffo, ma non funziona anche se avessi uno spazio dei nomi "ancora-un altro", che voleva anche ascoltare.

Quindi ho pensato di agganciare la prima funzione.

+0

Hmm, fare attenzione circa il termine "distruzione". Credo che la distruzione degli oggetti avvenga solo quando il GC si ripulisce. La rimozione di oggetti è diversa dalla distruzione di oggetti, sebbene uno possa causare l'altro. –

risposta

1

È questa soluzione adatta alle vostre esigenze?

graffi clj.first:

(ns scratch-clj.first) 

(def listf (atom [])) 
(def destroy-listeners (atom [])) 
(def add-listeners (atom [])) 

(defn add-destroy-listener [f] 
    (swap! destroy-listeners conj f)) 

(defn add-add-listener [f] 
    (swap! add-listeners conj f)) 

(defn add-object [] 
    (let [o (Object.)] 
    (doseq [f @add-listeners] (f o)) 
    (swap! listf conj o) 
    (println @listf))) 

(defn remove-object [] 
    (doseq [f @destroy-listeners] (f (first @listf))) 
    (swap! listf rest) 
    (println @listf)) 

Alcuni ascoltatori:

(ns scratch-clj.another 
    (:require [scratch-clj.first :as fst])) 

(defn do-something-useful-on-remove [object] 
    (println "object removed is:" object)) 

(defn do-something-useful-on-add [object] 
    (println "object added is:" object)) 

Init lega:

(ns scratch-clj.testit 
    (require [scratch-clj.another :as another] 
      [scratch-clj.first :as fst])) 

(defn add-listeners [] 
    (fst/add-destroy-listener another/do-something-useful-on-remove) 
    (fst/add-add-listener another/do-something-useful-on-add)) 

(defn test-it [] 
    (add-listeners) 
    (fst/add-object) 
    (fst/remove-object)) 

prova:

(test-it) 
=> object added is: #<Object [email protected]> 
    [#<Object [email protected]>] 
    object removed is: #<Object [email protected]> 
    () 
+0

Sì, penso di sì. Penso di poterlo fare anche con gli orologi, ma questo mi sembra un po 'più naturale, oltre a essere più generale. Grazie molto! –

+0

Prego. Ma non è ancora contrassegnato come corretto :( – mobyte

1

Sembra molto simile a quello che stai descrivendo è callback.

Qualcosa di simile:

(defn make-object 
    [destructor-fn] 
    {:destructor destructor-fn :other-data "data"}) 

(defn destroy-object 
    [obj] 
    ((:destructor obj) obj)) 

; somewhere at the calling code... 

user> (defn my-callback [o] (pr [:destroying o])) 
#'user/my-callback 
user> (destroy-object (make-object my-callback)) 
[:destroying {:destructor #<user$my_callback [email protected]>, :other-data "data"}] 
nil 
user> 
+0

Questa è un'idea ragionevole, ma sposta semplicemente il problema. Ora devo essere in grado di collegarmi alla funzione make-object, quindi posso aggiungere un distruttore. Prendiamo l'esempio sopra (aggiunto dopo il tuo commento, scusa!) - la creazione dell'oggetto e la distruzione stanno accadendo nello spazio dei nomi "prima" mentre il mio equivalente di callback è altrove. –

+0

Beh, detto così, uno dei problemi è che il clojure non funziona molto bene con l'idea di oggetti con distruttori. Generalmente avrai un tempo migliore se consideri (quasi) tutti i tuoi oggetti come record "stupidi". Dal tuo codice non vuoi vedere se l'oggetto è distrutto, vuoi fare un qualche tipo di azione ogni volta che la collezione in listf viene modificata. Gli orologi sono lo strumento ovvio per questo e non saranno probabilmente rimossi in qualunque momento presto. –

+0

In questo caso, gli oggetti in questione (come nel mio esempio) sono oggetti Java: sto usando clojure per manipolare un'API Java. Una volta rimosso dall'elenco, gli oggetti verranno distrutti. Un orologio di riferimento è garantito per essere chiamato? Non è veto, vero? –

0

Quindi, ecco la mia soluzione finale seguendo il suggerimento mobytes. Un po 'più di lavoro, ma Sospetto che lo vorrò in futuro.

Grazie per tutto l'aiuto

;; hook system 
(defn make-hook [] 
    (atom [])) 

(defn add-hook [hook func] 
    (do 
    (when-not 
     (some #{func} @hook) 
     (swap! hook conj func)) 
    @hook)) 

(defn remove-hook [hook func] 
    (swap! hook 
     (partial 
      remove #{func}))) 

(defn clear-hook [hook] 
    (reset! hook [])) 

(defn run-hook 
    ([hook] 
     (doseq [func @hook] (func))) 
    ([hook & rest] 
     (doseq [func @hook] (apply func rest)))) 

(defn phils-hook [] 
    (println "Phils hook")) 

(defn phils-hook2 [] 
    (println "Phils hook2")) 


(def test-hook (make-hook)) 
(add-hook test-hook phils-hook) 
(add-hook test-hook phils-hook2) 
(run-hook test-hook) 
(remove-hook test-hook phils-hook) 
(run-hook test-hook)