Clojure 如何在宏中正确使用valip谓词gt?

Clojure 如何在宏中正确使用valip谓词gt?,clojure,macros,clojurescript,Clojure,Macros,Clojurescript,我正在学习现代ClojureScript教程,并试图编写一些宏来自动化一些事情 例如,在其中一个验证函数中: (defn validate-shopping-form [quantity price tax discount] (validate {:quantity quantity :price price :tax tax :discount discount} ;; validate presence [:quantity pres

我正在学习现代ClojureScript教程,并试图编写一些宏来自动化一些事情

例如,在其中一个验证函数中:

(defn validate-shopping-form [quantity price tax discount]
  (validate {:quantity quantity :price price :tax tax :discount discount}

            ;; validate presence

            [:quantity present? "Quantity can't be empty"]
            [:price present? "Price can't be empty"]
            [:tax present? "Tax can't be empty"]
            [:discount present? "Discount can't be empty"]

            ;; validate type

            [:quantity integer-string? "Quantity has to be an integer number"]
            [:price decimal-string? "Price has to be a number"]
            [:tax decimal-string? "Tax has to be a number"]
            [:discount decimal-string? "Discount has to be a number"]

            ;; validate range

            [:quantity (gt 0) "Quantity can't be negative"]

            ;; other specific platform validations (not at the moment)

            ))  
我写了一些宏,但我的函数不编译。我不明白为什么

(ns try-cljs.shopping.validators
  (:require [valip.core :refer [validate]]
            [valip.predicates :refer [present?
                                      integer-string?
                                      decimal-string?
                                      gt]]))

(defmacro empty-checks []
  `(list ~@(map (fn [x] [(keyword x) 'present? (str x " can't be empty")])
                ["quantity" "price" "tax" "discount"])))

(defmacro number-checks []
  `(list ~@(mapv (fn [x] [(keyword x) 'decimal-string? (str x " should be a number")])
                 ["price" "tax" "discount"])))

(defmacro quantity-checks []
 `(list [:quantity integer-string? "quantity should be an integer"]
        [:quantity (gt 0) "quantity should be positive"]))

(defmacro checks [] `(list ~@(empty-checks)
                           ~@(number-checks)
                           ~@(quantity-checks)))

(defn validate-shopping-form [quantity price tax discount]
  (apply validate {:quantity quantity :price price :tax tax :discount discount}
                  (checks)))
错误是:
java.lang.IllegalArgumentException:找不到类valip.predicates$gt$fn\u 2332的匹配构造函数

它是关于
(gt 0)
表单的。但这意味着什么?我如何修复宏

顺便说一下。在REPL表达式中

(apply validate {:quantity "10"} (concat (quantity-checks)))

工作正常

首先要注意的是,您实际上不需要任何宏。我假设您想使用宏来了解宏

为了解释这个问题,我将使用您的检查功能的简化版本:

(defmacro checks [] `(list ~@(quantity-checks)))
当宏出现问题时,需要比较要生成的代码与宏实际生成的代码

要生成的内容:

(clojure.core/list
 [:quantity valip.predicates/integer-string? "quantity should be an integer"]
 [:quantity (valip.predicates/gt 0) "quantity should be positive"])
要查看实际生成的内容,请使用
(clojure.pprint/pprint(macroexpand'(检查))

#object[valip.predicates$gt$fn\uu 25100…
胡言乱语是
(gt 0)
返回的fn的toString表示

因此,您的宏正在执行
(gt 0)
而不是返回表示调用
(gt 0)
的代码。这是因为
数量检查
是一个宏,因此在编译
检查
宏之前会对其进行评估。有关详细说明,请参阅

要避免宏执行函数,只需引用表达式:

(defmacro quantity-checks []
  `(list [:quantity 'integer-string? "quantity should be an integer"]
         [:quantity '(gt 0) "quantity should be positive"]))
如果使用此版本运行macroexpand,您将看到它生成的代码与我们想要的代码相同

请注意,数量检查可以是以下功能:

(defn quantity-checks []
 `([:quantity integer-string? "quantity should be an integer"]
   [:quantity (gt 0) "quantity should be positive"]))

反向引用并不排除宏。

您使用的是哪个版本的valip?这是一个.cljc文件吗?@nberger版本是0.4.0-SNAPSHOT,而文件是.cljc。您的宏检查会在宏展开时评估其他宏。
中的
~
检查的就是这样做的。这导致了错误,因为评估
数量-检查
使
(gt 0)
被评估,这就是你的有效性。谓词$gt$fn_u2332。但你不想这样,你想在运行时输出
(gt 0)
进行评估。我想你可以用
(空检查)(数字检查)(数量检查))
而不是
(列表~@(空检查)…)
或者可能只是删除了检查宏中的三个
~
。我找到了一些解决方案。但是由于运行时
concat
调用,它非常难看。但是我仍然不明白为什么原始代码不起作用。请你澄清我的另一个错误理解,好吗?我已经像这里一样重写了我的验证代码。它是有效的正如Clojure中所预期的那样,但是ClojureScript给出了错误:
不知道如何从文件src/cljc/try_cljs/shopping/validators.cljc第30行第32列的Clojure.lang.Symbol中创建ISeq。问题是什么?Clojure println是什么?它只是在末尾用换行符打印内容。这些带有
println
的行可以被注释掉,但错误在第31行再次出现。
(defn quantity-checks []
 `([:quantity integer-string? "quantity should be an integer"]
   [:quantity (gt 0) "quantity should be positive"]))