请在2024 Clojure调查问卷!分享您的想法。

欢迎!请参见关于页面了解更多信息。

0
分类 Spec

我在尝试使用自定义函数生成器以支持spec指南中描述的场景https://clojure.org/guides/spec#_combining_check_and_instrument,但目标是使生成的值依赖于具体的参数,而不仅仅是依赖`:ret` spec。

在处理这个问题时,我发现了一个奇怪的行为,就是在函数返回之前,生成的函数被调用了21次。

(require '[clojure.spec.alpha :as s]
         '[clojure.spec.gen.alpha :as gen])

(s/fdef foo
  :args (s/cat :x int?)
  :gen #(gen/return
         (fn [& argv]
           (prn argv)
           (gen/generate (s/gen string?)))))

(gen/generate (s/gen `foo))
;; Prints:
;; (-1)
;; (-1)
;; (-2)
;; (-1)
;; (3)
;; (-2)
;; (-2)
;; (-1)
;; (-35)
;; (14)
;; (-16)
;; (26)
;; (-17)
;; (150)
;; (0)
;; (-1)
;; (25)
;; (5638)
;; (543)
;; (57)
;; (257)
;; Returns: #function[fspec-gen-bug.core/fn--6577/fn--6578]

这种行为似乎仅针对函数生成,例如

(s/def ::bar
  (s/spec (s/coll-of int?)
          :gen #(gen/return
                 (doto [(gen/generate (s/gen int?))]
                   prn))))

(gen/generate (s/gen ::bar))
;; Prints:
;; [438803]
;; Returns: [438803]

为什么生成的函数要积极调用?或者这是一个bug?

1 答案

0

经过一些额外的调查,我了解到当通过s/valid?生成函数时,会调用s/conform*,并且21是默认的*fspec-iterations*值。

我对在函数生成时使用全面instrumentation(即使用`:args`、`:ret`和`:fn`)而只有在函数执行时使用`:args`的设计选择感到有些惊讶。

但经过一番思考,既然每个函数都与一个生成器自动相关联,因此生成不满足spec中的`:fn`属性的函数是一个重要的事情。此外,生成函数的目的是作为占位符进行检查,因此在执行时仅检查`:args`对于正确地进行错误边界检查也是一件好事。

我们在规范2中有一个工单和一些关于此的替代实现的思路。
...