使用clojure spec验证运行时传递给我的函数的匿名函数

时间:2018-08-12 09:28:19

标签: clojure 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
 )

那是明智的选择还是从哲学的角度讲有意义?

1 个答案:

答案 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传递的功能的可能范围。