使用 clojure 规范验证在运行时传入我的函数的匿名函数
Validating anonymous functions passed in to my function at runtime, with clojure spec
假设我有一个函数 a
,它接受一个函数 (fn
) 作为参数:
(defn a [f] ....)
.
为了给调用者一个很好的错误消息,我会在运行时验证 fn
参数。
这可能吗,我该怎么做?我可以只在运行时 fdef
这个,然后在调用时检测它吗,比如:
(defn a [f]
;;.... spec f here on the fly, and call f, throw exception if f
;; does not conform to spec
)
从哲学的角度来看,这是明智的还是有意义的?
Can I just at runtime fdef this, and then instrument it upon calling [...]
不,因为 instrument
接受一个符号,解析它的 var,并实质上用一个包装原始函数的新函数替换它——这就是验证输入参数的工作完成的地方。在您的示例中,您将无权访问 f
函数的 "original" 符号,并且匿名函数不会解析为 var.
您可以指定高阶函数,因此您可以这样指定 a
:
(defn a [f] (f (rand-int)))
(s/fdef a :args (s/cat :f (s/fspec :args (s/cat :x number?))))
(st/instrument `a)
(a inc) ;; => 2
(a str) ;; => "1"
(a (fn [x] (assoc x :foo 'bar))) ;; spec error b/c fn doesn't conform
但是... 请注意,作为 f
传递给检测 a
的任何函数都将使用随机输入调用多次!这就是 spec 确定函数是否符合的方式。
(a #(doto % prn inc))
-1
-0.75
...
=> 0.18977464236944408
显然你不想将它与副作用或昂贵的功能一起使用。我只建议对 testing/development 使用 instrument
。您也可以在函数中使用 s/assert
,但这仍会多次调用 f
。
(s/def ::f (s/fspec :args (s/cat :x number?)))
(defn a [f]
(s/assert ::f f)
(f (rand)))
(s/check-asserts true)
Is that wise or does it make sense from a philosophical stand point?
这实际上取决于您的程序的性质以及作为 f
传递的函数的可能域。
假设我有一个函数 a
,它接受一个函数 (fn
) 作为参数:
(defn a [f] ....)
.
为了给调用者一个很好的错误消息,我会在运行时验证 fn
参数。
这可能吗,我该怎么做?我可以只在运行时 fdef
这个,然后在调用时检测它吗,比如:
(defn a [f]
;;.... spec f here on the fly, and call f, throw exception if f
;; does not conform to spec
)
从哲学的角度来看,这是明智的还是有意义的?
Can I just at runtime fdef this, and then instrument it upon calling [...]
不,因为 instrument
接受一个符号,解析它的 var,并实质上用一个包装原始函数的新函数替换它——这就是验证输入参数的工作完成的地方。在您的示例中,您将无权访问 f
函数的 "original" 符号,并且匿名函数不会解析为 var.
您可以指定高阶函数,因此您可以这样指定 a
:
(defn a [f] (f (rand-int)))
(s/fdef a :args (s/cat :f (s/fspec :args (s/cat :x number?))))
(st/instrument `a)
(a inc) ;; => 2
(a str) ;; => "1"
(a (fn [x] (assoc x :foo 'bar))) ;; spec error b/c fn doesn't conform
但是... 请注意,作为 f
传递给检测 a
的任何函数都将使用随机输入调用多次!这就是 spec 确定函数是否符合的方式。
(a #(doto % prn inc))
-1
-0.75
...
=> 0.18977464236944408
显然你不想将它与副作用或昂贵的功能一起使用。我只建议对 testing/development 使用 instrument
。您也可以在函数中使用 s/assert
,但这仍会多次调用 f
。
(s/def ::f (s/fspec :args (s/cat :x number?)))
(defn a [f]
(s/assert ::f f)
(f (rand)))
(s/check-asserts true)
Is that wise or does it make sense from a philosophical stand point?
这实际上取决于您的程序的性质以及作为 f
传递的函数的可能域。