specter

Latest version: 1.1.3
vigilancetech 2018-07-12T14:39:25.000047Z

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?

nathanmarz 2018-07-12T14:49:46.000538Z

@vigilancetech can you show the code?

vigilancetech 2018-07-12T14:52:11.000556Z

(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))))

vigilancetech 2018-07-12T14:52:33.000207Z

(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))

vigilancetech 2018-07-12T14:55:28.000646Z

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

nathanmarz 2018-07-12T15:01:44.000458Z

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

nathanmarz 2018-07-12T15:03:26.000142Z

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

nathanmarz 2018-07-12T15:15:57.000604Z

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

nathanmarz 2018-07-12T15:16:02.000527Z

since map is lazy

vigilancetech 2018-07-12T15:22:17.000282Z

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> 

nathanmarz 2018-07-12T15:23:16.000616Z

the repl realizes lazy sequences when it prints it

nathanmarz 2018-07-12T15:23:57.000210Z

the way you wrote checkbox-checked will not

nathanmarz 2018-07-12T15:24:15.000020Z

better to write that sort of code with doseq

vigilancetech 2018-07-12T15:25:26.000599Z

ah, okay

vigilancetech 2018-07-12T15:25:39.000458Z

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

nathanmarz 2018-07-12T15:26:42.000240Z

don't know what you mean by that

nathanmarz 2018-07-12T15:27:03.000229Z

or what those other function references are

vigilancetech 2018-07-12T15:28:58.000563Z

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

vigilancetech 2018-07-12T15:29:27.000627Z

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

vigilancetech 2018-07-12T15:30:13.000389Z

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

vigilancetech 2018-07-12T15:31:13.000458Z

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

vigilancetech 2018-07-12T15:32:22.000104Z

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

vigilancetech 2018-07-12T15:37:05.000166Z

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

alexyakushev 2018-07-12T15:38:14.000206Z

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

alexyakushev 2018-07-12T15:38:28.000571Z

Try replacing map with mapv, would it work?

vigilancetech 2018-07-12T15:41:10.000279Z

nope, same thing

nathanmarz 2018-07-12T15:53:51.000147Z

where is it appending multiple root tags?

vigilancetech 2018-07-12T15:54:37.000422Z

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")))

vigilancetech 2018-07-12T15:54:59.000307Z

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

vigilancetech 2018-07-12T15:56:18.000193Z

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

nathanmarz 2018-07-12T15:57:27.000604Z

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

vigilancetech 2018-07-12T15:57:40.000457Z

yes

nathanmarz 2018-07-12T15:58:09.000170Z

what does it look like before and after?

vigilancetech 2018-07-12T15:58:30.000355Z

the results are in the code display above.

nathanmarz 2018-07-12T15:59:37.000017Z

I think you're reading the repl wrong

nathanmarz 2018-07-12T15:59:43.000007Z

the sequence there is the result of map

nathanmarz 2018-07-12T15:59:55.000192Z

which is showing the iterative changes to ui-state

nathanmarz 2018-07-12T16:00:02.000473Z

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

alexyakushev 2018-07-12T16:00:17.000145Z

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

vigilancetech 2018-07-12T16:00:55.000190Z

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

nathanmarz 2018-07-12T16:01:38.000581Z

just do @ui-state

vigilancetech 2018-07-12T16:01:41.000325Z

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

vigilancetech 2018-07-12T16:02:13.000480Z

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

alexyakushev 2018-07-12T16:02:42.000243Z

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

alexyakushev 2018-07-12T16:03:16.000531Z

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

alexyakushev 2018-07-12T16:03:33.000091Z

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

vigilancetech 2018-07-12T16:04:35.000574Z

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

alexyakushev 2018-07-12T16:04:44.000236Z

Yep

alexyakushev 2018-07-12T16:04:58.000180Z

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

alexyakushev 2018-07-12T16:05:10.000386Z

That's like mapc in Lisp

nathanmarz 2018-07-12T16:05:13.000591Z

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

vigilancetech 2018-07-12T16:05:59.000518Z

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

vigilancetech 2018-07-12T16:06:58.000148Z

thanks guys! Really loving this tool