2014-02-16 46 views
1

我在這裏有一點困惑,我無法弄清楚。我試圖創建一些函數,除了幾件事情(包括它們所採用的參數的數量)之外,它們都非常相似。多次使用Clojure宏來生成函數

我寫了一個宏來創建這些似乎正常工作的函數之一。下面是宏

(defmacro gen-fn 
    [meth args transform-fns] 
    `(fn [~'conn [email protected]] 
    (->> (. metadata-service 
      ~meth 
      (sec/single-use-ticket ~'conn) 
      [email protected]) 
      [email protected]))) 

這樣我就可以正確地創建兩個函數

(def get-source (gen-fn getSource [version source] [bean])) 
(def get-all-sources (gen-fn getAllSources [version] [(map bean)])) 

和雙方工作時,我打電話給他們這樣的:

(get-source conn "2013AB" "WHO97") 
(get-all-sources conn "2013AB") 

現在我有這些功能的約600至如果我可以簡化這一點(最終可能在應用程序啓動時從外部源讀取它),那麼創建它將會很好。所以,我在這裏的第一個想法是構建一個地圖是這樣的:

(def metadata-methods 
    { "getSources" [["version" "source"] ["bean"]] 
    "getAllSources" [["version"] ["(map bean)"]] }) 

或沿着這些線路的東西,然後使用doseq或類似的東西來創建功能

(doseq [[function-label [args transform-fns]] metadata-methods] 
    (intern *ns* (symbol (->kebab-case function-label)) 
       (gen-fn function-label [version source] [bean]))) 

當我運行這一點,似乎工作,但調用(get-source conn "2013AB" "WHO97")拋出一個異常,說沒有匹配的方法「function_label」類代理...

因此,某種程度上宏沒有正確創建函數。

所以我的問題是 1)有沒有簡單的方法來完成這項工作? 2)我是否做了比它需要更復雜的東西?有沒有更簡單的方法來完成同樣的事情?

一個純函數將不同的是各功能要產生的事實工作需要不同數目的參數,我真的希望每個的功能爲具有固定的參數數量。

+1

這可以幫助你:https://github.com/swannodette/om/blob/master/src/om/dom.clj – ClojureMostly

回答

3

宏作爲參數傳遞的宏調用的實際參數表達式,所以在這個呼叫:

(gen-fn function-label [version source] [bean]) 

gen-fn將被傳遞的實際符號function-label,兩個符號的向量和一個符號的矢量作爲論據。所以這就是爲什麼get-source不與doseq方式工作。

通常的方法來完成這樣的事情是定義像你gen-fn和其他宏宏,說def-fns(以下使用原始宏的名字的複數形式的版本,並改變gen的通常模式def因爲我們」重新創建瓦爾),以發射多個gen-fn形式包裝在do

(defmacro def-fns [& args] 
    `(do [email protected](for [[meth args transform-fns] (partition 3 args) 
       :let [name (symbol (->kebab-case (str meth)))]] 
      `(def ~name (gen-fn ~meth ~args ~transform-fns))))) 

然後說

(def-fns 
    getSource [version source] [bean] 
    getAllSources [version] [(map bean)]) 

如果你想用的地圖,這也是有可能的:

;; def the map first 
(defmacro def-fns [] 
    `(do [email protected](for [[meth [args transform-fn]] metadata-methods 
       :let [name (symbol (->kebab-case meth))] 
      `(def ~name (gen-fn ~meth ~args ~transform-fn))))) 

注意,宏功能將使用metadata-methods編譯時的值(這是罰款)。

+0

這是太棒了,米哈爾!我很感激。你教了我很多在這個過程中了。 –