在宏中定义函数:不能使用限定名作为参数

时间:2015-10-25 18:07:56

标签: clojure

我想要一个宏来定义返回它们被称为的形式的函数,例如: (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 ~@body) list

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

2 个答案:

答案 0 :(得分:4)

您不能使用合格的符号作为功能参数。观察

`body

评估为current-namespace/body

在语法引用中,您始终可以取消引用非语法引用以获取不合格的符号:

`~'body

评估为body。 (请注意,此处的unquoting用于评估内部引用本身)。

但是,在这种情况下,您应该生成一个符号,因为如果用户在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)))))

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