This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2017-08-25
Channels
- # aws (1)
- # bangalore-clj (1)
- # beginners (15)
- # boot (4)
- # clara (7)
- # cljs-dev (7)
- # cljs-experience (3)
- # cljsrn (1)
- # clojure (143)
- # clojure-austin (2)
- # clojure-germany (1)
- # clojure-italy (11)
- # clojure-serbia (11)
- # clojure-spec (96)
- # clojure-uk (20)
- # clojurescript (70)
- # community-development (58)
- # cursive (14)
- # data-science (1)
- # datomic (45)
- # events (2)
- # fulcro (19)
- # jobs (5)
- # jobs-rus (2)
- # off-topic (40)
- # om (24)
- # onyx (3)
- # parinfer (52)
- # pedestal (6)
- # protorepl (38)
- # re-frame (15)
- # reagent (11)
- # ring-swagger (5)
- # specter (37)
- # sql (3)
- # unrepl (3)
- # vim (1)
@nathanmarz here's my use case, see if you think Specter makes this easier. I'm building a library for modular behaviour trees. i've decided to represent these as nested vectors a la Hiccup markup so, for example [:selector [:loop {:count 99} [:sequence [:randomly {:p 0.5} [:foo]] [:randomly {:p 0.25} [:bar]] [:baz]]]]
what i want to do is (1) label each vector with an autogenerated id, (2) check that required options are specified (e.g. :loop
must always have a :count
option) (3) check required number of children is specified (e.g. :loop
always has exactly 1 child)
i have code doing this based on postwalk (well i've not done 3 yet but it's a trivial modification of 2)
at the moment I am hoisting the options map (and the id) into the metadata for the vector
i'm undecided whether a better approach would be to add an options map to all vectors and put the id in there instead
@sandbags yes, this is exactly the kind of stuff specter excels at
i'll show you an example
(one thing that the walk approach does not seem to make particularly easy is giving diagnostic information)
(def data [:selector [:loop {:count 99} [:sequence [:randomly {:p 0.5} [:foo]] [:randomly {:p 0.25} [:bar]] [:baz]]]])
(def ALL-VECTORS
(recursive-path [] p
(if-path vector?
(continue-then-stay ALL p))))
(let [data2 (transform (subselect ALL-VECTORS META :id) (fn [s] (-> s count range)) data)]
(select [ALL-VECTORS (collect-one META :id)] data2))
;; =>
[[0 [:foo]]
[1 [:randomly {:p 0.5} [:foo]]]
[2 [:bar]]
[3 [:randomly {:p 0.25} [:bar]]]
[4 [:baz]]
[5
[:sequence
[:randomly {:p 0.5} [:foo]]
[:randomly {:p 0.25} [:bar]]
[:baz]]]
[6
[:loop
{:count 99}
[:sequence
[:randomly {:p 0.5} [:foo]]
[:randomly {:p 0.25} [:bar]]
[:baz]]]]
[7
[:selector
[:loop
{:count 99}
[:sequence
[:randomly {:p 0.5} [:foo]]
[:randomly {:p 0.25} [:bar]]
[:baz]]]]]]
that's adding the auto-generated id to the metadata of each vector, the select
call is just so you can see what it did
here's how you could insert ids at index 1 of each vector:
(transform (subselect ALL-VECTORS (srange 1 1)) (fn [s] (->> s count range (map vector))) data)
;;=> [:selector 7 [:loop 6 {:count 99} [:sequence 5 [:randomly 1 {:p 0.5} [:foo 0]] [:randomly 3 {:p 0.25} [:bar 2]] [:baz 4]]]]
and as one last example, here's how you would manipulate a particular node type in the tree:
(defn node-type [kw] (path (selected? FIRST (pred= kw))))
(transform [ALL-VECTORS (node-type :randomly) (nthpath 1) :p] #(* 2 %) data)
;; => [:selector [:loop {:count 99} [:sequence [:randomly {:p 1.0} [:foo]] [:randomly {:p 0.5} [:bar]] [:baz]]]]
it's really a shame Specter isn't better known. I know about it and a number of times i've messed around for hours with walk/zippers etc. trying to do something that would have been easier with a bit of Specter
I consider it essential for immutable programming
I've encountered a surprising amount of resistance to it though
i guess one downside (but probably just inherent in this kind of complex structure bending) is that those expressions take some real picking apart
I wouldn't consider conciseness a downside, but the opposite
definitely a learning curve
what you're seeing here is the result after me learning this stuff for years
it's a different way of thinking
but it brings true simplicity and an incredible amount of power over your data
no idea, I'm curious about that myself
they haven't said anything about it to my knowledge
I suggested contributing specter to clojure awhile back, but the response from core team was "no" with minimal explanation https://groups.google.com/forum/#!searchin/clojure/contribute$20specter%7Csort:relevance/clojure/qN1UPMVQmaM/uegIimKjCwAJ
also saw this recently which makes me think rich would have serious problems with specter: https://gist.github.com/reborg/dc8b0c96c397a56668905e2767fd697f
since he's philosophically against the same function having different performance characteristics on different datatypes, he won't like how I designed the navigators
nthpath
for instance being O(n) on lists but faster on vectors (O(log n) I think)