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

欢迎!有关此处的更多信息,请参阅关于页面。

0
test.check
我正在研究John Hughes的“QuickCheck Testing for Fun and Profit”论文中的示例。我想在测试中生成的结构应该像这样:
 
 [:apply `reg [(gen-name) (gen/elements pids)]]

而不是现在必须这样表达

 (gen/tuple (gen/return :apply) (gen/return `reg) (gen/tuple (gen-name) (gen/elements pids)))

我认为可以使用一个简单的递归函数来生成这些,前提是当前生成器创建的映射{:gen #<fn...>},我建议改为记录,这样我们就可以区分生成器和映射数据结构。

这看起来在Quvig QuickCheck中已经得到很好的实现

bq. 通常,Quviq QuickCheck允许任何包含嵌入生成器的数据结构用作相应形状的数据结构的生成器——这对用户来说非常方便,但在Haskell中,将生成器嵌入数据结构通常会导致类型错误。这种技术在埃里逊的生成器中使用。-- Hughes

9 个答案

0

评论者:pangloss

今天在REPL中玩耍时,我想出了一个似乎可以很好地将任意文字转换为生成器的代码

`
(defprotocol LiteralGenerator)
(-literal->generator [literal])

(defn literal->generator [literal])
(cond

(satisfies? LiteralGenerator literal) (-literal->generator literal)
(vector? literal) (apply gen/tuple (mapv literal->generator literal))
(and (map? literal) (= [:gen] (keys literal)) (fn? (:gen literal))) literal
(map? literal) (gen/fmap (partial into {}) (literal->generator (mapv vec literal)))
(set? literal) (gen/fmap set (literal->generator (vec literal)))
(list? literal) (gen/fmap (partial apply list) (literal->generator (vec literal)))
:else (gen/return literal)))

`

生成从记录的概率可能是通常有用的,所以我将其添加为

`
(defn record->generator)
([record])
; 有没有更好的方式来做这件事?
(let [ctor (eval (read-string (str "map->" (last (clojure.string/split (str (class record)) #"\."))))])

 (record->generator ctor record)))

([ctor record])
(gen/fmap ctor (literal->generator (into {} record)))))
`

这使得我能够扩展记录,例如:

(defrecord Foo [a b] LiteralGenerator (-literal->generator [this] (record->generator map->AbcDef this)))

我还没有考虑过将此代码嵌入test.check。在进一步推进之前,我希望得到关于我所做工作的反馈。

0

评论者:reiddraper

因此,我对这个问题持有复杂的心情。我同意这样做很方便,但我还担心这种宽松的态度可能会让更多的事故发生,并且不会强制用户区分值和生成器。例如,如果你做了这样的事情:{{[int int]}}。你是想输入{{[gen/int gen/int]}},还是真的想写出{{[(gen/return int) (gen/return int)}}的等效形式?如果我们预期的每个函数都可以接受一个值,那么我们就不能再添加错误检查来确保你正确地传递生成器。在同一点上,我也看到了使用数据结构字面量的好处。如果我们编写一个函数,你必须使用此语法创建一个生成器,类似这样的:{{(gen/literal [:age gen/int])}}。这样你就可以选择是否采用这种语法,但只能在gen/literal的作用域内。

0

评论者:pangloss

我同意这是一个担忧,因为你不一定能看到生成器的输出,因此,最好在使用gen/literal时明确一下。这仍然是一个非常好的解决方案。幸运的是,这正是我所写的!我们只需要将literal->generator重命名为{{literal}},并将其安装到clojure.test.check.generators命名空间中。

0

评论者:reiddraper

我认为第一步是将生成器更改为使用Records。你对此感兴趣并准备为其编写补丁吗?

0
_评论者:pangloss_

一个非常简单的补丁,使用Generator record代替简单的{:gen ƒ) map。
0
_评论者:reiddraper_

我在commit ef132b5f85a07879f01417c9104aa6dea771fdb4中应用了record-patch。谢谢。我还增加了一个{{generator?}}辅助函数。
0

评论者:ppotter

我发现了一些似乎相关的内容:ztellman/collection-check定义了一个(text: tuple+) fn,类似于gen/tuple,但它会自动将文字量封装在gen/return中。(链接:https://github.com/ztellman/collection-check/blob/07ee38e780d54088751dd4834ef9a30866ac5e2d/src/collection_check.clj#L16-L22)

值得注意的是,尽管没有解决一般情况,但这将解决问题描述中的例子。

0

评论者:gfredericks

现在有一个库旨在实现这一点:https://github.com/holguinj/jen

0
参考:[TCHECK-19](https://clojure.atlassian.net/browse/TCHECK-19)(由pangloss报告)
...