This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2018-10-12
Channels
- # beginners (58)
- # boot (4)
- # calva (1)
- # cider (13)
- # cljdoc (1)
- # cljs-dev (7)
- # cljsrn (14)
- # clojure (93)
- # clojure-canada (1)
- # clojure-conj (1)
- # clojure-germany (1)
- # clojure-italy (6)
- # clojure-losangeles (3)
- # clojure-nl (8)
- # clojure-spec (6)
- # clojure-uk (77)
- # clojurescript (3)
- # cursive (5)
- # data-science (6)
- # datomic (52)
- # emacs (1)
- # figwheel-main (2)
- # fulcro (6)
- # graphql (7)
- # jobs (9)
- # leiningen (1)
- # luminus (15)
- # mount (14)
- # off-topic (94)
- # pedestal (1)
- # re-frame (7)
- # reagent (10)
- # shadow-cljs (75)
- # spacemacs (4)
- # test-check (15)
- # tools-deps (23)
- # unrepl (1)
In this snippet, frequency
evals render
too early. I think I need mutually recursive generators, and don’t want to lose the ability to shrink. If it actually ran it wouldn’t go on forever, but instead it explodes the stack. Anybody have any ideas?
(letfn [(render []
(tcgen/frequency [[1 (gen/tuple (render))]
[15 (gen/return [])]]))]
(gen/generate (render)))
frequency is a function, so all the arguments are evaluated before the function is called
@taylor I distilled my problem down to this.
I am generating a tree where any given node’s children’s generator is dependent on the node that produces it. Also, some nodes must have children, and some must not. Also some of the data in the child needs to be consistent with what was generated the parent. Then the thing that breaks it is some nodes can have children of the same type, which use the same generator.
I am trying to make a generator for the whole tree.
So a generator for a node might produce something like: {:id 1 :parent-id 0 :type :foo}
Nodes of type :foo
can have children of types :bar
and :foo
.
The generators for the potential children of this node would produce something that looks like: {:id 2 :parent-id 1 :type :foo}
or {:id 2 :parent-id 1 :type :bar :another-quality "junk"}
are the child nodes associated inside the parent nodes? or are they all in one big sequence and only correlated by :id
and :parent-id
?
You could use recursive-gen to get the structure, then fmap the whole thing to fix up consistency issues
(defn type-foo [g]
(gen/let [children (gen/vector g)]
(gen/return
{:id 1 :parent-id 0 :type :foo :children children})))
(defn type-bar [g]
(gen/return
{:id 2 :parent-id 1 :type :bar :another-quality "junk"}))
(defn foo-bar-tree [_]
(gen/recursive
(fn [g]
(gen/one-of (type-foo g)
(type-bar g)))
(type-bar nil)))
I will say often times it is easier to generate instructions to build a complex datastructure then it is to generate the datastructure. so for example if you wanted a tree of files in directories, it might be easier to generate a list of create file, move file, create directory, delete, etc, then just interpret that list to get some tree, instead of generating the tree
@sean even though you want a flat sequence of maps, you could maybe do something like this with recursive-gen:
(gen/sample
(gen/recursive-gen
(fn [g]
(gen/let [{:keys [id type] :as m} (s/gen ::my-map)
children (gen/vector g)]
(if (= :bar type)
m
(update m :children concat (map #(assoc % :parent-id id) children)))))
(s/gen ::my-map))
100)
then you could fmap
over that generator to flatten and fix-up the ID relations:
(gen/sample
(gen/fmap
(fn [m]
(map #(dissoc % :children)
(tree-seq #(seq (:children %)) :children m)))
(gen/recursive-gen
(fn [g]
(gen/let [m (s/gen ::my-map)
children (gen/vector g)]
(if (= :bar (:type m))
m
(update m :children concat (map #(assoc % :parent-id (:id m)) children)))))
(s/gen ::my-map))))
although I think this still leaves you with a problem of non-unique IDs