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

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

0
h2. 问题

动机问题是实现 {{gen/let}} 在 {{test.check}} 中(也可以参考 TCHECK-98)。

{{gen/let}} 的通用用法可能如下所示


(gen/let [a gen-a
          b gen-b]
  (f a b))


此代码的关键特性是 {{b}} 的生成器不依赖于 {{a}} 的值(虽然一般情况下可能如此)。由于这种独立性,理想的展开是


(gen/fmap
  (fn [[a b]] (f a b))
  (gen/tuple gen-a gen-b))


然而,因为 {{gen/let}} 无法在一般情况下判断 {{b}} 生成器的表达式是否依赖于 {{a}},它需要回退到一个更通用的展开


(gen/fmap
  (fn [[a b]] (f a b))
  (gen/bind
    gen-a
    (fn [a]
      (gen/tuple (gen/return a) gen-b))))


使用 {{gen/bind}} 会大幅降低缩放能力,因此最好能避免使用。

有经验的用户可以通过显式地使用 {{gen/tuple}} 来解决问题,例如


(gen/let [[a b] (gen/tuple gen-a gen-b)]
  (f a b))


但我认为大多数用户更希望不用去想这些。

h2. 可能的解决方案

h3. tools.analyzer

{{tools.analyzer}} 可能足够用,但这是一个大型库的依赖。

h3. tools.analyzer 的子集

Nicola 提到了从分析器中切割出一些足够用于此情况的子集的想法,这可能是最好的选择。

h3. 宏展开宏体的机制

我相信,如果有一个健壮的机制让宏完整地宏展开一个表达式,这个问题会更容易解决({{clojure.core/macroexpand}} 和朋友有一些已知的不正确性)-- 简单地对扩展表达式进行 {{tree-seq}} 可以证明没有使用局部变量(虽然直接的方法可能会错误地得出结论说局部变量 **被使用**了,这在 test.check 的案例中可能是一个可接受的妥协,否则在扩展代码上实现健壮的代码遍历器不应很困难)。

我认为 zach's [riddley|https://github.com/ztellman/riddley] 库做了类似的事情,依赖于 riddley 可能是非贡献库的最佳选择,但不是贡献库的可接受的依赖。

1 答案

0
by
参考:https://clojure.atlassian.net/browse/CLJ-1997 (由 gfredericks 报告)
...