如何在宏中正确使用 valip predicate gt?
How to correctly use valip predicate gt with macros?
我正在学习现代 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: No matching ctor found for class valip.predicates$gt$fn__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 '(checks)))
:
(clojure.core/list
[:quantity #object[valip.predicates$integer_string_QMARK_ 0x13272c8f "valip.predicates$integer_string_QMARK_@13272c8f"] "quantity should be an integer"]
[:quantity #object[valip.predicates$gt$fn__25100 0x17fe6151 "valip.predicates$gt$fn__25100@17fe6151"] "quantity should be positive"])
#object[valip.predicates$gt$fn__25100...
乱码是 (gt 0)
返回的 fn 的 toString 表示形式。
因此您的宏正在执行 (gt 0)
,而不是返回表示对 (gt 0)
的调用的代码。这是因为 quantity-check
是一个宏,所以在编译 checks
宏之前对其求值。有关详细说明,请参阅 this question。
为了避免宏执行函数,你只需要引用表达式:
(defmacro quantity-checks []
`(list [:quantity 'integer-string? "quantity should be an integer"]
[:quantity '(gt 0) "quantity should be positive"]))
如果您 运行 使用此版本进行宏扩展,您会看到生成的代码与我们想要的代码相同。
请注意 quantity-checks 可以是这样的函数:
(defn quantity-checks []
`([:quantity integer-string? "quantity should be an integer"]
[:quantity (gt 0) "quantity should be positive"]))
Back-quoting不排除宏。
我正在学习现代 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: No matching ctor found for class valip.predicates$gt$fn__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 '(checks)))
:
(clojure.core/list
[:quantity #object[valip.predicates$integer_string_QMARK_ 0x13272c8f "valip.predicates$integer_string_QMARK_@13272c8f"] "quantity should be an integer"]
[:quantity #object[valip.predicates$gt$fn__25100 0x17fe6151 "valip.predicates$gt$fn__25100@17fe6151"] "quantity should be positive"])
#object[valip.predicates$gt$fn__25100...
乱码是 (gt 0)
返回的 fn 的 toString 表示形式。
因此您的宏正在执行 (gt 0)
,而不是返回表示对 (gt 0)
的调用的代码。这是因为 quantity-check
是一个宏,所以在编译 checks
宏之前对其求值。有关详细说明,请参阅 this question。
为了避免宏执行函数,你只需要引用表达式:
(defmacro quantity-checks []
`(list [:quantity 'integer-string? "quantity should be an integer"]
[:quantity '(gt 0) "quantity should be positive"]))
如果您 运行 使用此版本进行宏扩展,您会看到生成的代码与我们想要的代码相同。
请注意 quantity-checks 可以是这样的函数:
(defn quantity-checks []
`([:quantity integer-string? "quantity should be an integer"]
[:quantity (gt 0) "quantity should be positive"]))
Back-quoting不排除宏。