2017-06-28 56 views
4

我在REPL中編寫了多方法,函數可以重新定義,但如果我重新定義多方法的調度函數,它似乎不使用新改進的函數:如何在Clojure REPL中重新加載多方法REPL

;; simple fn to resolve defmethod to call, hardcoded to :do-it 
(defn resolve-it [] :do-it) 
(resolve-it) ;; :do-it, as expected 

(defmulti do-something resolve-it) 

(defmethod do-something :do-it [] (println "do-it")) 
(defmethod do-something :oh-no [] (println "oh-no")) 

(do-something) ;; "do-it", as expected 

;; now change resolve-it 
(defn resolve-it [] :oh-no) 
(resolve-it) ;; :oh-no, as expected 

(do-something) ;; "do-it", not as expected 

(do-something) ;; "do-it", not expected 

如何獲得反映調度功能更改的多方法resolve-it

回答

1

看起來像defmulti正在緩存調度功能。這裏是你的代碼的修改版本,說明了這個問題:

;; simple fn to resolve defmethod to call, hardcoded to :do-it 
(defn who-is-it [person] (:name person)) 
(spyx (who-is-it {:name :joe})) 

(defmulti do-something who-is-it) 
(defmethod do-something :homer [person] :doh) 
(defmethod do-something :bill [person] :oh-no) 
(defmethod do-something :ted [person] :excellent) 

(spyx (do-something {:name :homer})) 
(spyx (do-something {:name :bill})) 

;; now change who-is-it 
(defn who-is-it [arg] :ted) 
(spyx (who-is-it :wilma)) ;; expected result = :excellent 
(spyx (do-something {:name :betty})) 

與結果:

:reloading (tst.clj.core) 
(who-is-it {:name :joe}) => :joe 
(do-something {:name :homer}) => :doh 
(do-something {:name :bill}) => :oh-no 
(who-is-it :wilma) => :ted 
:error-while-loading tst.clj.core 

Error refreshing environment: java.lang.IllegalArgumentException: No method in multimethod 'do-something' for dispatch value: :betty, compiling:(tst/clj/core.clj:22:27) 

看起來你可能需要重新初始化REPL重新定義調度FN。與我們看到預期的行爲的新會話

(defmulti do-something who-is-it) 
(defmethod do-something :homer [person] :doh) 
(defmethod do-something :bill [person] :oh-no) 
(defmethod do-something :ted [person] :excellent) 

(spyx (do-something {:name :betty})) ;=> ***same error *** 
Error refreshing environment: java.lang.IllegalArgumentException: No method in multimethod 'do-something' for dispatch value: :betty, compiling:(tst/clj/core.clj:30:1) 

這裏:

;; simple fn to resolve defmethod to call, hardcoded to :do-it 
(defn who-is-it [person] (:name person)) 
(spyx (who-is-it {:name :joe})) 

;; now change who-is-it 
(defn who-is-it [arg] :ted) 
(spyx (who-is-it :wilma)) ;; expected result = :ted 
; (spyx (do-something {:name :betty})) 

(defmulti do-something who-is-it) 
(defmethod do-something :homer [person] :doh) 
(defmethod do-something :bill [person] :oh-no) 
(defmethod do-something :ted [person] :excellent) 

(dotest 
    (spyx (do-something {:name :betty}))) 

(do-something {:name :betty}) => :excellent ; *** as expected *** 

更新

我試過ns-unmap技術Rumid描述和它的作品甚至重複一切並沒有爲我改寫do-something也。我注意到,你必須重新發出defmulti所有defmethod聲明:

(ns-unmap *ns* 'do-something) ; be sure to remember the quote 
(defmulti do-something who-is-it) 
(defmethod do-something :homer [person] :doh) 
(defmethod do-something :bill [person] :oh-no) 
(defmethod do-something :ted [person] :excellent) 

(dotest 
    (newline) 
    (spyx (do-something {:name :betty}))) ;=> :excellent 
+0

哪裏是spyx從? – Kris

3

根據clojuredocs exmapledefmulti不會讓您重新定義它。你必須從命名空間中取消映射do-somethingns):

(ns-unmap *ns* 'do-something)

並重新分配它,就如同以前那樣:

(defmulti do-something resolve-it) 
(defmethod do-something :do-it [] (println "do-it")) 
(defmethod do-something :oh-no [] (println "oh-no")) 
+1

這可以起作用,我可以將'ns-unmap'放在文件的頂部,作爲實驗目的,當我將文件發送到nREPL時,所做的更改與預期相同。 – Kris

8

有一個簡單的技術,它允許重新定義調度多方法的功能。這個想法是將包含調度函數的var傳遞給defmulti,而不是函數本身。請注意0​​的defmulti而不是resolve-it。因此,var在運行時被取消引用,而不僅僅是在編譯時。

(defn resolve-it [] :do-it) 
(resolve-it) ;; :do-it, as expected 

(defmulti do-something #'resolve-it) 

(defmethod do-something :do-it [] (println "do-it")) 
(defmethod do-something :oh-no [] (println "oh-no")) 

(do-something) ;; "do-it", as expected 

;; now change resolve-it 
(defn resolve-it [] :oh-no) 
(resolve-it) ;; :oh-no, as expected 

(do-something) ;; "oh-no", expected!! 
+0

這適合在REPL中進行實驗。 – Kris