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

欢迎!请参阅关于页面,获取有关如何工作的更多信息。

0投票
test.check
我在阅读 John Hughes 的“QuickChek 测试:乐趣与利润”论文中的示例时,我希望我在这些测试中生成的结构看起来像这样
 
 [: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

我认为第一步是改变生成器以使用记录。你有没有兴趣为这个问题编写一个补丁?

0投票
_评论者:pangloss_

一个非常简单的补丁,使用生成器记录而不是简单的{:gen ƒ)映射。
0投票
评论由: reiddraper_ 添加

我已经在 ef132b5f85a07879f01417c9104aa6dea771fdb4 中应用了记录修复。谢谢。我还增加了一个 {{generator?}} 辅助函数。
0投票

评论由: ppotter 添加

我发现了一些似乎相关的内容:ztellman/collection-check 定义了一个 fn,如 gen/tuple 但自动将文字用 gen/return 包装。(链接:https://github.com/ztellman/collection-check/blob/07ee38e780d54088751dd4834ef9a30866ac5e2d/src/collection_check.clj#L16-L22 文本:tuple*)

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

0投票

评论由: gfredericks 添加

现在有一个库专注于这个问题:https://github.com/holguinj/jen

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