Clojure 如何在宏中正确使用valip谓词gt?
我正在学习现代ClojureScript教程,并试图编写一些宏来自动化一些事情 例如,在其中一个验证函数中: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
(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"]))