如何在Clojure中做钩子

时间:2012-11-27 15:54:22

标签: clojure hook

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

在Java中,我可以使用一个监听器。不幸的是,底层的java库不会发出对象破坏事件的信号。如果我在Emacs-Lisp中,那么我会使用钩子来做这个伎俩。

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

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

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

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

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

已添加示例:

So First namespace ....

(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必须要求另一个并明确地推送删除事件。这有点笨拙,但如果我有“又一个”命名空间,也想听,也行不通。

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

3 个答案:

答案 0 :(得分: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> 

答案 1 :(得分: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>
   ()

答案 2 :(得分: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)