假设我有一个生成器users-gen
,它生成一组 1 个或多个用户。另一个称为参数化的生成器user-actions-gen
,它接受一个或多个用户的序列,并生成这些用户可能执行的一系列操作。
(def user-gen
;; generates a user
...)
(def users-gen
;; sequences of 1 or more users
(gen/such-that not-empty (gen/vector gen/users))
(defn user-actions-gen [users]
;; a action performed by some user from the `users argument
...)
如果我想为 users-gen 生成的单个用户序列生成单个 action,那么很简单,直接 gen/bind users-gen 到 user-actions-gen 即可。
但是,我想从相同的用户序列中生成许多操作。我有这个问题,因为我基本上只是想说“这是状态,让任何随机动作进来,让我们将动作应用于状态,让我们确认状态仍然有效;对所有动作都这样做。 " 我有以下代码。
(defspec check-that-state-is-always-valid
100
(let [state-atm (atom {})]
(prop/for-all
[[actions users]
(gen/bind users-gen
(fn [users]
(gen/tuple
(gen/vector (user-actions-gen users))
(gen/return users))))]
(doseq [action actions
:let [state (swap! state-atm state-atm-transform-fx action)]]
(is (state-still-valid? state))))))
这类作品。问题是:
- 它似乎完全评估了doseq,而不是在第一个错误时停止
- 只是看起来有点不对劲。代码到处都是,它的作用并不完全清楚。
- 似乎 user-actions-gen 应该采用 users-gen 的生成器,而不是 users-gen 的已实现用户价值?这对可组合性有帮助吗?请注意,我不想将它们放在一起,因为 users-gen 可能对其他生成器有用。
所以,回顾一下。我从一个生成器获取单个生成的值,并将其作为参数传递给多个生成器。我该如何以更有吸引力/优雅的方式来做这件事?