使用Clojure Spec生成用于命名参数的两组键的并集的生成器

时间:2017-04-13 09:38:59

标签: clojure clojure.spec

让我们看看clojure.spec/merge

的Clojure Spec Guide中给出的示例
(require '[clojure.spec     :as spec]
         '[clojure.spec.gen :as gen])

(spec/def :animal/kind string?)
(spec/def :animal/says string?)
(spec/def :animal/common (spec/keys :req [:animal/kind :animal/says]))
(spec/def :dog/tail? boolean?)
(spec/def :dog/breed string?)
(spec/def :animal/dog (spec/merge :animal/common
                                  (spec/keys :req [:dog/tail? :dog/breed])))

根据此规范,我们既可以生成数据又可以验证它:

(gen/generate (spec/gen :animal/dog))
=> {:animal/kind "bB", :animal/says "z9C0T465Q8OPXn5dUB8Wqk8K5Jnn",
    :dog/tail? false, :dog/breed "B2MLQnj"}

(spec/valid? :animal/dog
             {:animal/kind "bB", :animal/says "z9C0T465Q8OPXn5dUB8Wqk8K5Jnn",
              :dog/tail? false, :dog/breed "B2MLQnj"})
=> true

但是,如果我们稍微修改规范,以便它是一系列命名参数而不是地图,比如

(spec/def :animal/common (spec/keys* :req [:animal/kind :animal/says]))
(spec/def :animal/dog (spec/merge :animal/common
                                  (spec/keys* :req [:dog/tail? :dog/breed])))

,我们仍然可以根据规范验证数据:

(spec/valid? :animal/dog
             '(:animal/kind "dog"
               :animal/says "woof"
               :dog/tail?   true
               :dog/breed   "retriever"))
=> true

但我们确实失去了生成数据的能力:

(gen/generate (spec/gen :animal/dog))

; 1. Unhandled clojure.lang.ExceptionInfo
;    Couldn't satisfy such-that predicate after 100 tries.

这是我的错误,规范中的实现错误,还是clojure.spec/merge的工作方式?我们可以通过附加发电机来解决这个问题吗?

1 个答案:

答案 0 :(得分:1)

看看规范/合并的实现,似乎有一个特殊情况用于生成地图,但不是用于生成键/值对的序列。我猜这是因为它仍然是alpha,甚至还没有API稳定,更不用说完全实现了。提供你自己的发电机似乎可以做到这一点。例如:

(gen/generate (spec/gen :animal/dog {:animal/dog #(clojure.test.check.generators/return '(:animal/kind "2qAW61r3030B", :animal/says "7k", :dog/tail? true, :dog/breed "00Y8C9T25cRrSQsnjOn26a"))}))