2016-02-08 29 views
1

我試圖找到一種方法來通過函數列表線程值。如何將一個列表傳遞給clojure的` - >`宏?

首先,我有一個常用的基於環代碼:

(defn make-handler [routes] 
    (-> routes 
     (wrap-json-body) 
     (wrap-cors) 
     ;; and so on 
    )) 

但是,這不是最佳的,因爲我想寫一個測試,以檢查路由實際上總結CORS包裹。我決定將這些包裝解壓縮成一個def。因此,代碼變成如下:

(def middleware 
    (list ('wrap-json-body) 
     ('wrap-cors) 
     ;; and so on 
     )) 

(defn make-handler [routes] 
    (-> routes middleware)) 

這顯然不工作,不應該爲->宏不拿名單的第二個參數。所以我試圖用apply功能來解決:

(defn make-handler [routes] 
    (apply -> routes middleware)) 

最終與保釋出來:

CompilerException了java.lang.RuntimeException:無法採取的宏觀 值:#」 clojure.core/- >

於是問題出現了:一個人如何傳遞值的列表中->宏(或者說,任何其他宏)作爲一個將與apply的功能嗎?

+1

也許這是一個重複和[在clojure中,如何將宏應用於列表?](https://stackoverflow.com/questions/9273333/in-clojure-how-to-apply-a-macro-一個清單)回答你的問題。 – sloth

+0

@sloth,謝謝你指出一個。雖然它提供了關於如何解決問題的一些想法,但我更喜歡leetwinsky的答案,因爲它更符合我的口味和乾淨。 – eploko

+4

' - >'的要點是讓代碼更易於閱讀。但是,如果你只是寫一個新的宏,以便你可以使用' - >'(在代碼中你永遠不會看到,因爲它只存在於宏擴展中),但我沒有看到這一點。本着從不使用宏的精神,我建議採用以下解決方案:'(減少#(%2%)路由中間件)' – galdre

回答

2

,你可以爲一個宏:

;; notice that it is better to use a back quote, to qoute function names for macro, as it fully qualifies them. 
(def middleware 
    `((wrap-json-body) 
    (wrap-cors)) 
    ;; and so on 
    ) 

(defmacro with-middleware [routes] 
    `(-> ~routes [email protected])) 

例如這樣的:

(with-middleware [1 2 3]) 

將擴展到這一點:

(-> [1 2 3] (wrap-json-body) (wrap-cors)) 
+1

固定錯誤,未加引號的'routes' – leetwinski

6

這是一個XY問題

->的要點是使代碼更易於閱讀。但是,如果僅僅爲了使用->而寫一個新的宏(在代碼中沒有人會看到,因爲它只存在於宏擴展中),但在我看來,這是做了很多工作沒有任何好處。而且,我認爲它掩蓋了,而不是澄清了代碼。

所以,從來不使用宏在那裏功能會做的精神,我建議以下兩種等價的解決方案:

解決方案1 ​​

(reduce #(%2 %) routes middleware) 

解決方案2

((apply comp middleware) routes) 

更好的方法

第二溶液很容易通過從被的功能的列表改變middleware的定義爲的函數的composition簡化:

(def middleware 
    (comp wrap-json-body 
      wrap-cors 
      ;; and so on 
     )) 

(middleware routes) 

當我開始學習的Clojure,我碰到這種模式運行往往不夠,我的很多早期項目有核心定義的freduce

(defn freduce 
    "Given an initial input and a collection of functions (f1,..,fn), 
    This is logically equivalent to ((comp fn ... f1) input)." 
    [in fs] 
    (reduce #(%2 %) in fs)) 

這是完全不必要的,有的人可能喜歡直接用reduce爲b更清楚。但是,如果您不喜歡在應用程序代碼中注意#(%2 %),那麼在您的語言中添加另一個實用程序字就可以了。

+0

@gladre,函數的組成不會做。將其全部提取出來的唯一目的是測試所應用的中間件集合。我錯過了什麼,並且有一種方法可以知道哪些函數是用'comp'組成的? – eploko

+0

我不確定我是否理解這個問題。通過查看傳遞給'comp'的參數,你可以知道哪些fns是用'comp'組成的。如果您想以編程方式檢查fns,則將中間件定義爲列表,並使用'reduce'(解決方案1)或'apply comp'(解決方案2)。正常情況下,comp'ed fns會拋出異常,並帶有堆棧跟蹤。如果你想在中間插入一個測試,那麼列表方法將無法工作。如果你看看' - >'和'comp'的源代碼,你會發現它們執行完全相同的操作嵌套,所以對於程序流程它們應該是相同的。 – galdre

+0

如果你想通過編程檢查'middleware'來查看它包含的函數,'(reduce middleware ...)'將比'comp'的一次性應用程序少做工作。 – galdre