1

假设我有一个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
 )

从哲学的角度来看,这是明智的还是有意义的?

4

1 回答 1

3

我可以在运行时 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都将使用随机输入多次调用!这就是规范如何确定函数是否符合要求。

(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.

于 2018-08-12T15:37:16.257 回答