Fork me on GitHub
#specter
<
2018-07-12
>
vigilancetech14:07:25

I got this strange situation: I'm using specter from clojurescript (hoplon) to build up a map in a function which when I feed it by hand works fine, but when I map values into it, it munges the map up. Is mapping it making it try and do the operations in parallel somehow? If so, how would I serialize them?

nathanmarz14:07:46

@vigilancetech can you show the code?

vigilancetech14:07:11

(defn stow-effect! [dev effect]
  (let [x (sp/setval [:lights (id->type dev) (id->key dev)] effect (:root @ui-state))
        y (sp/setval [:lights :all (id->key dev)] effect x)]
    ;(s/set-ui-state! @conn y)
    ;; there's some kind of issue with multi-processing here
    (reset! ui-state (sp/setval [:root] y @ui-state))))

vigilancetech14:07:33

(defn checkbox-checked [device-type]
  (let [x (map vals (sp/select [(n-device-records device-type) (sp/submap [:id :effect])] @data))]
    (map #(apply stow-effect! %) x)
    x))

vigilancetech14:07:28

when I feed it with the second function it makes multiple :root tags, but when I feed it by hand it works properly making only one

nathanmarz15:07:44

what is n-device-records, id->type, id->key?

nathanmarz15:07:26

and what do you mean by "makes multiple :root tags"?

nathanmarz15:07:57

doesn't look like (map #(apply stow-effect! %) x) will do anything

nathanmarz15:07:02

since map is lazy

vigilancetech15:07:17

cljs.user> (s/set-ui-state! @d/conn nil )
nil
cljs.user> (s/get-ui-state @d/conn)
nil
cljs.user> @d/ui-state
{:root nil}
cljs.user> (d/checkbox-checked :fan)
(("Fan 0" "roll") ("Fan 1" "static_color") ("Fan 2" "none") ("Fan 3" "none") ("Fan 4" "none") ("Fan 5" "none"))
cljs.user> @d/ui-state
{:root nil}
cljs.user> (s/get-ui-state @d/conn)
nil
cljs.user> @d/ui-state
{:root nil}
cljs.user> (d/stow-effect! "Fan 0" "roll")
{:root {:lights {:all {:Fan_0 "roll"}, :fan {:Fan_0 "roll"}}}}
cljs.user> @d/ui-state
{:root {:lights {:all {:Fan_0 "roll"}, :fan {:Fan_0 "roll"}}}}
cljs.user> (d/stow-effect! "Fan 1" "static_color")
{:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color"}, :fan {:Fan_0 "roll", :Fan_1 "static_color"}}}}
cljs.user> (d/stow-effect! "Fan 2" "none")
{:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none"}}}}
cljs.user> (d/stow-effect! "Strip 0" "police")
{:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Strip_0 "police"}, :strip {:Strip_0 "police"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none"}}}}
cljs.user> (s/set-ui-state! @d/conn nil )
nil
cljs.user> (s/get-ui-state @d/conn)
nil
cljs.user> @d/ui-state
{:root nil}
cljs.user> (map #(apply d/stow-effect! %) '(("Fan 0" "roll") ("Fan 1" "static_color") ("Fan 2" "none") ("Fan 3" "none") ("Fan 4" "none") ("Fan 5" "none")))
({:root {:lights {:all {:Fan_0 "roll"}, :fan {:Fan_0 "roll"}}}} {:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color"}, :fan {:Fan_0 "roll", :Fan_1 "static_color"}}}} {:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none"}}}} {:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_3 "none"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_3 "none"}}}} {:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_4 "none", :Fan_3 "none"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_4 "none", :Fan_3 "none"}}}} {:root {:lights {:all {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_5 "none", :Fan_4 "none", :Fan_3 "none"}, :fan {:Fan_0 "roll", :Fan_1 "static_color", :Fan_2 "none", :Fan_5 "none", :Fan_4 "none", :Fan_3 "none"}}}})
cljs.user> 

nathanmarz15:07:16

the repl realizes lazy sequences when it prints it

nathanmarz15:07:57

the way you wrote checkbox-checked will not

nathanmarz15:07:15

better to write that sort of code with doseq

vigilancetech15:07:39

think that'll fix the multiple :root tag thing?

nathanmarz15:07:42

don't know what you mean by that

nathanmarz15:07:03

or what those other function references are

vigilancetech15:07:58

(defn n-device-records [type] [:lights sp/ALL #(= (:type %) type)])

vigilancetech15:07:27

(defn id->key [id] (keyword (st/replace id \space \_)))

vigilancetech15:07:13

(defn id->type [id] (let [x (st/split (st/lower-case id) #" ")]
                      (keyword (if (> (count x) 2)
                                 (str (first x) \_ (second x))
                                 (first x)))))

vigilancetech15:07:13

n-device-records navigates to all the records for a certain type of device. The others just convert from a string to a (spaceless) tag

vigilancetech15:07:22

thing is when I feed stow-effect! by hand, it builds ui-state properly with only one root tag, but when I map the values in, it screws it up, appending multiple root tags

vigilancetech15:07:05

could that be a specter thing? Or does that have something to do with map parallelizing the operation?

alexyakushev15:07:14

In fact, map can't "parallelize" anything.

alexyakushev15:07:28

Try replacing map with mapv, would it work?

vigilancetech15:07:10

nope, same thing

nathanmarz15:07:51

where is it appending multiple root tags?

vigilancetech15:07:37

when I do:

(mapv #(apply d/stow-effect! %) '(("Fan 0" "roll") ("Fan 1" "static_color") ("Fan 2" "none") ("Fan 3" "none") ("Fan 4" "none") ("Fan 5" "none")))

vigilancetech15:07:59

the result has multiple root tags, but if I feed those values in by hand, it works correctly, with only one root tag

vigilancetech15:07:18

its supposed to be {:root {:lights {:all {all records} {:fan {fan devices} :strip {strip devices.}}}}}

nathanmarz15:07:27

by result you mean the value of the atom ui-state?

nathanmarz15:07:09

what does it look like before and after?

vigilancetech15:07:30

the results are in the code display above.

nathanmarz15:07:37

I think you're reading the repl wrong

nathanmarz15:07:43

the sequence there is the result of map

nathanmarz15:07:55

which is showing the iterative changes to ui-state

nathanmarz16:07:02

the final value in that sequence is the final value of ui-state

alexyakushev16:07:17

I'd suggest you minimize the reproducible case, it's really hard to follow without the context and the CLJS at hand.

vigilancetech16:07:55

oh, so maybe it IS working and the repl is just printing out intermediate results

nathanmarz16:07:38

just do @ui-state

vigilancetech16:07:41

yup, you're right. When I check ui-state its correct. Thanks. That had me baffled

vigilancetech16:07:13

seems like most (lisp) repls I've used before only show the final return value

alexyakushev16:07:42

Since you are doing map, you get the list of values anyway.

alexyakushev16:07:16

The final form of stow-effect! is a reset! call

alexyakushev16:07:33

reset! sets a new value for an atom and returns that value too

vigilancetech16:07:35

so that's where the accumulation is happening; yeah, makes sense now, since I'm taking the side effect of that map rather than its result

alexyakushev16:07:58

Also, if you need map for side effects only, you can use run! instead

alexyakushev16:07:10

That's like mapc in Lisp

nathanmarz16:07:13

btw, you can clean up this code with ATOM and view navigators

vigilancetech16:07:59

yeah, I'm a total noob to specter (and only slightly less so to clojure)

vigilancetech16:07:58

thanks guys! Really loving this tool