2024 Clojure 状态调查 中分享您的想法!

欢迎!请参阅 关于 页面以了解更多有关该工作方式的信息。

0
规格

我在尝试定制函数生成器来支持 spec 教程中描述的情景,但目标是生成的值依赖于具体参数,而不仅仅是 :ret 规范。

在处理这个问题时,我注意到一个奇怪的行为,即在返回之前生成函数被调用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* 值。

我对在设计选择中使用完全仪器检查(即在函数生成时使用 :args、:ret 和 :fn,在函数执行时仅使用 :args)感到有些惊讶。

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

我们在规范2中有一个票据和一些关于该替代实现的思路。
...