Meikel Brandmeyer committed 49c4938

Add generators and transform

Comments (0)

Files changed (1)


   (boolean 0.5))
-;; ## let-gen
+;; ## `let-gen` and `generators`
 ;; `let-gen` was supposed to provide some domonad style interface
 ;; for easy building of custom generators. However this seriously
             `(SimpleValue. ~expr)
             (partition 2 (rseq bindings)))))))
+(declare transform)
+(defmacro generators
+  "Takes a map of locals to generators. Upon generation of a value, binds
+  the generated values to the locals and executes body to obtain the final
+  value. Binding happens in parallel—similar to `binding`.
+  Shrinking is handled by shrinking each generator one after the other
+  and executing the body with the shrunken values in place."
+  {:added "2.1"}
+  [bindings & body]
+  (let [ks  (keys bindings)
+        gen (zipmap (map (fn [k] `(quote ~k)) ks) (vals bindings))]
+    `(transform ~gen (fn [{:syms [~@ks]}] ~@body))))
+(defn transform
+  "Generates a value according to `gen` and calls `f` with the generated
+  value—not the domain value. The return value of `f` becomes the new
+  value.
+  Shrinking is handled by shrinking the original domain value and
+  mapping `f` over the resulting sequence of shrunken values."
+  {:added "2.1"}
+  [gen f]
+  (let [transformer (fn transformer [dval]
+                      (ShrinkableValue. (SimpleValue. (f (value dval)))
+                                        (fn [_]
+                                          (map transformer (shrink dval)))))]
+    (reify
+      Generator
+      (arbitrary [this size] (transformer (generate gen size))))))
 (defn frequency
   "Chooses one of the given generators based on the associated
   weights. The size guidance is passed verbatim to the chosen