说我有一个函数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
)
那是明智的选择还是从哲学的角度讲有意义?
答案 0 :(得分:2)
我可以在运行时fdef这个吗,然后在调用[...]
时对其进行检测
否,因为instrument
接受一个符号,解析其var,并从本质上将其替换为一个包装原始变量的新函数-这就是完成验证输入参数的工作。在您的示例中,您将无法访问f
函数的“原始”符号,并且匿名函数不会解析为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
很显然,您不希望将其与副作用较大或昂贵的功能一起使用。我只建议使用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)
那是明智的选择还是从哲学的角度讲有意义?
这实际上取决于程序的性质以及作为f
传递的功能的可能范围。