我正在使用Modern 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)
形式的。但是这意味着什么以及如何修复宏?
(apply validate {:quantity "10"} (concat (quantity-checks)))
工作正常
答案 0 :(得分:1)
首先要注意的是,你真的不需要任何宏。我假设您想使用宏来了解宏。
为了解释这个问题,我将使用检查功能的简化版本:
(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...
gibberish是(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"]))
如果您使用此版本运行macroexpand,您将看到生成与我们想要的代码相同的代码。
请注意,数量检查可以是以下函数:
(defn quantity-checks []
`([:quantity integer-string? "quantity should be an integer"]
[:quantity (gt 0) "quantity should be positive"]))
后引用不包括宏。