在此功能中:
(defn my-post
[a]
{:post (number? %)}
a)
后置条件不执行(或至少不会导致断言错误)。我现在知道应该是:
(defn my-post
[a]
{:post [(number? %)]} ;; note the square brackets around the expression
a)
事实上,哪种方法可以正常工作。
问题在于,这种方式无声无息,并花了一些时间来弄清楚出了什么问题。没有语法错误,运行时异常。
我想了解Clojure对此代码的处理方式,以了解为什么Clojure没有抱怨。宏扩展?解构?如果没有看到方括号,代码是否会消失?
答案 0 :(得分:5)
http://clojure.org/special_forms说明fn
的条件地图(因此也是defn
)应采用以下形式:
{:pre [pre-expr*]
:post [post-expr*]}
{:post (number? %)}
将导致(number? %)
被视为一系列断言,这意味着它被解释为两个独立的断言:number?
和%
。
user> (macroexpand-1 '(fn [a] {:post (number? %)} a))
(fn*
([a]
(clojure.core/let [% a]
(clojure.core/assert number?)
(clojure.core/assert %)
%)))
只要(assert number?)
被定义并且具有真值, number?
总是通过,它可能是核心函数。如果(clojure.core/assert %)
具有真值,则会%
通过。它通过a
与您的参数let
的值绑定,因此如果a
具有真值,则它会通过。尝试使用您的第一个函数定义调用(my-post nil)
,它将无法通过断言。
user> (my-post nil)
; Evaluation aborted.
; Assert failed: %
; [Thrown class java.lang.AssertionError]
如果你将你的后置条件正确地放在一个向量中,它会像这样扩展:
user> (macroexpand-1 '(fn [a] {:post [(number? %)]} a))
(fn*
([a]
(clojure.core/let [% a]
(clojure.core/assert (number? %))
%)))