5

我有一种情况,我在一个 clojure 命名空间中创建和销毁对象,并希望另一个命名空间进行协调。但是,我不希望第一个命名空间必须在对象销毁时显式调用第二个命名空间。

在 Java 中,我可以使用监听器。不幸的是,底层的 java 库不会在对象销毁时发出事件信号。如果我在 Emacs-Lisp 中,那么我会使用钩子来解决问题。

现在,在clojure中我不太确定。我找到了 Robert Hooke 库https://github.com/technomancy/robert-hook。但这更像是 elisp 术语中的 defadvice——我正在编写函数。更多的文档说:

“钩子旨在扩展您无法控制的功能;如果您拥有目标功能,显然有更好的方法来改变它的行为。”

可悲的是,我发现它并不那么明显。

另一种可能性是使用 add-watch,但这被标记为 alpha。

我错过了另一个明显的解决方案吗?

示例添加:

所以第一个命名空间....

(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)

第二个命名空间

(ns scratch-clj.another)


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

这里的问题是,scratch-clj.first 必须要求另一个并明确地推送删除事件。这有点笨拙,但如果我有“又一个”命名空间,它也想听,也不起作用。

因此,我想到了挂钩第一个功能。

4

3 回答 3

1

该解决方案是否适合您的要求?

从头开始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))

一些听众:

(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))

初始化绑定:

(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))

测试:

(test-it)
=> object added is: #<Object java.lang.Object@c7aaef>
   [#<Object java.lang.Object@c7aaef>]
   object removed is: #<Object java.lang.Object@c7aaef>
   ()
于 2012-11-27T17:41:11.730 回答
1

听起来很像您所描述的回调。

就像是:

(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 user$my_callback@73b8cdd5>, :other-data "data"}]
nil
user> 
于 2012-11-27T16:15:31.603 回答
0

所以,这是我遵循 mobytes 建议的最终解决方案。更多的工作,但我怀疑我将来会想要这个。

感谢所有的帮助

;; 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)
于 2012-11-27T22:09:50.673 回答