2015-10-25 38 views
5

我想要一個宏來定義返回它們被調用的形式的函數。 (func 1 (a b))返回(func 1 (a b))。我也想允許這些函數的輸入驗證,以確保我沒有引入任何錯誤。 (這些表格稍後會被評估,但該代碼尚未寫入。)在宏中定義一個函數:不能使用限定名作爲參數

雖然我不斷收到此錯誤。

(defmacro defecho 
    "Echo function call after asserting a few things about the input" 
    ([f] `(defecho ~f nil nil)) 
    ([f assertions] `(defecho ~f assertions nil)) 
    ([f assertions assert-failed-message] 
    `(defn ~f [& body]    ; define a function 
     ~(when-not (nil? assertions) ; if given a function for input validation 
     `(assert (~assertions body) ; define the function to assert this as true 
        ~assert-failed-message)) ; with a given error message 
     (conj body (quote ~f)))))  ; return the (f [email protected]) list 

(defecho my-test 
    #(< 2 (count %)) 
    "Must be greater than zero") 
  • Unhandled clojure.lang.Compiler$CompilerException 
    Error compiling: 
    /private/var/...228.clj:1:1 
    Can't use qualified name as parameter: my-test/body 
    
  • Caused by java.lang.RuntimeException 
    Can't use qualified name as parameter: my-test/body 
    
  • 回答

    4

    您不能使用合格的符號作爲函數參數。觀察

    `body 
    

    評估爲current-namespace/body

    在語法報價,你可以隨時解除引用非語法報價讓一個不合格的符號:

    `~'body 
    

    評估爲body。 (請注意,此處不加引用評估內部引用本身)。

    但是,在這種情況下,您應該生成一個符號,因爲如果用戶在e中使用符號body。 g代碼assert-failed-message你不希望他的綁定body與你的影子(觀察他的代碼是否在實際調用生成的函數時進行評估)。

    常見的做法是,以產生用於這一目的的符號,或者使用gensym或哈希,這句法報價將擴大到gensym通話結束的象徵..

    `body# 
    

    計算結果爲(不合格! )符號body__34343__auto__其中數字在每次調用時不同,並且保證每次都不相同。

    由於您從兩個不同的語法引用中引用了body,因此我選擇gensym選項與let的組合,以便只生成一個符號。

    (defmacro defecho ; overloads stripped for brevity 
        [f assertions assert-failed-message] 
        (let [args-sym (gensym "body")] ; define a symbol for function arglist 
        `(defn ~f [& ~args-sym]    ; define a function 
         ~(when-not (nil? assertions)  ; if given a function for input validation 
          `(assert (apply ~assertions ~args-sym) ; define the function to assert this as true 
            ~assert-failed-message)) ; with a given error message 
         (conj ~args-sym (quote ~f))))) 
    
    +0

    您還可以使用'body#'語法自動gensym名稱。 – drnewman

    +0

    @drnewman由於示例在兩個不同的語法引用上下文中使用'body#',因此會生成不同的符號。 –

    +0

    Leon Grapenthin,你說得對,謝謝你的糾正 – drnewman

    0

    您可以通過使用合適的功能,爲繁重的工作使您的生活更簡單一點,使用宏僅語法糖:

    (defmacro defecho 
        "Echo function call after asserting a few things about the input" 
        ([f] `(defecho ~f nil nil)) 
        ([f assertions] `(defecho ~f assertions nil)) 
        ([f assertions assert-failed-message] 
        `(def ~f (echo-function (quote ~f) ~assertions ~assert-failed-message)))) 
    
    (defn echo-function [f assertion assert-failed-message] 
        (fn [& body] 
        (when assertion 
         (assert (assertion body) 
           assert-failed-message)) 
        (conj body f)))