Fork me on GitHub
#clojure
<
2015-08-01
>
henrik09:08:06

Let’s say that I have an atom containing a set, and I want to react to changes in that set. Is there a way to add a watch that only triggers if the contents of the atom were actually changed? Or will I have to perform a diff on every update the figure this out?

darwin10:08:39

@henrik: in your watch handler use identical? to detect changes on interested paths in the atom, it is fast

darwin10:08:23

I have just solved similar problem with reagent reactions in my project, I had thousands of watch handlers which became slow, solved it this way: https://github.com/darwin/plastic/blob/master/cljs/src/main/plastic/reagent/sonar.cljs#L29-L36

darwin10:08:07

Instead of letting reagent register thousands of watch handler on atom, I register one watch handler, maintain a map of paths and when watch handler is triggered I go recursively from root and prune the tree with identical?, then call my real change handlers only if some paths I was interested in really changed

henrik11:08:21

@darwin, awesome, thank you! I just realized I wrote something similar (yet different) over here, but I didn’t use identical?. Would that improve the performance do you think? https://github.com/eneroth/plato/blob/master/src/plato/core.cljs#L13-L27

darwin11:08:00

@henrik: identical? is just a pointer compare, you are doing deep walk and construction of difference, which is definitely slower

darwin11:08:12

btw. I think you implemented a subset of https://clojuredocs.org/clojure.data/diff

henrik11:08:45

@darwin, true, but the idea is to terminate when branches are identical, so it would make sense to use it I guess.

darwin11:08:19

= equality does deep comparison

henrik11:08:14

@darwin, yes, that was absolutely the idea (to implement an altered version of diff). Because of the way data is stored in local storage, it doesn’t make sense to look for diffs in vectors (since they are stored as strings anyway).

henrik11:08:56

@darwin, I will definitely change this. Thanks again!

darwin11:08:31

identical? may be tricky, depends how you update your atom, for me this discussion was helpful: https://github.com/reagent-project/reagent/pull/143

darwin11:08:41

again, it is reagent related, but the same root problem

darwin11:08:13

sometimes you can update atom with value which is equal to the old one, but not identical?

darwin11:08:24

because it was constructed from scratch

darwin11:08:07

so maybe in case of shallow, small structures you actually want to be doing equality test instead of identical?

darwin11:08:34

if you don’t want to potentially rewrite all code which modifies the structure

darwin11:08:01

btw. you can look at my overwrite-map helper, which is paying for equality test only during write, if equal it does not break identity

henrik11:08:29

Hm, I guess that given that identical? returns false, one could check with =, if the performance benefits of not re-rendering are greater than potentially checking the entire data structure. When identical? returns true, the search tree will still be pruned.

henrik11:08:58

@darwin, cheers, I’ll have a look!

henrik11:08:55

@darwin, yep, this code:

(def test-atom (atom #{:one :two}))

(add-watch test-atom :test-watch #(when-not (identical? %3 %4)
                                    (println "Old: " %3 " New: " %4)))

(swap! test-atom conj :one)
prints Old: #{:one :two} New: #{:one :two}, just as you predicted

henrik11:08:48

… in CLJS, but not in CLJ…

henrik11:08:20

I guess this comes down to the difference in how identical? works in cljs vs. clj

darwin12:08:16

ah, this is CLJ channel, I don’t know much about how this behaves in CLJ

ikhthiandor12:08:33

263 p;[\kop41441plhn6y-----+9

maxp16:08:49

how to implement java's wait(timeout)/notify in Clojure? please help.

maxp16:08:51

I mean one thread sleeps on the mutex and wakes up after timeout or event

potetm16:08:24

@maxp: I would recommend using core.async. But if that’s not available, you can always just use Java’s facilities directly.

potetm16:08:04

In other words, do a line-for-line replication of your Java code to clojure.

maxp16:08:43

There is no timeouted read in core.asynt

potetm16:08:53

@maxp: (a/alts! [my-data-chan (a/timeout 1000)])

potetm16:08:03

This article has a section on using alts! for timeouts: http://www.braveclojure.com/core-async/#4__Choice

johanna-belanger18:08:52

If I have a map like this {:title "whatever" :group {:section1 {:tag1 "this" :tag2

johanna-belanger18:08:10

Oops pressed send by accident

johanna-belanger18:08:44

Trying again If I have a map like this: {:title " whatever " :group {:section1 {:t1 "a" :t2 "b"} :section2 {:t1 "c" :t2 "d"}} :footer "etc"} What is the best way to transform it to {:title " whatever " :section1 {:t1 "a" :t2 "b"} :section2 {:t1 "c" :t2 "d"} :footer "etc"} , stripping out the :group?

shaunxcode18:08:53

(merge (dissoc x :group) (:group x))

johanna-belanger18:08:50

Oh perfect thanks!

Pablo Fernandez18:08:12

When using defschema, how do you mark a field as optional?

zoldar19:08:28

@pupeno: s/maybe if I'm not mistaken

zoldar19:08:13

oh, just saw the timestamp - you have probably figured it out already

arohner19:08:47

s/maybe is for values. If you want a key in a map to be optional, there’s s/optional-key

Pablo Fernandez22:08:23

In compojure-api, when defining the :body-params, how can I specify that some are optional?

Pablo Fernandez22:08:37

I tried :body-params [x :- Long, y :- (s/maybe Long)] and :body-params [x :- Long, (s/optional-key y) :- Long] but neither worked.

arohner22:08:56

@pupeno: that’s specifying that body should be a vector. Is that what you intend?

arohner22:08:21

I don’t know what vector w/ optional-key does, but it’s probably not good

Pablo Fernandez22:08:35

@arohner: ah… no, that’s not what I want.

arohner22:08:10

try :body-params {x :- Long, (s/optional-key y) :- Long}

Pablo Fernandez22:08:08

arohner: how would it look like if only x was required? {x :- Long}?

Pablo Fernandez22:08:43

Isn’t that a hashmap with odd number of elements?

Pablo Fernandez22:08:40

arohner: the vector doesn’t mean that the api will receive a vector, it’s the way parameters are described.

Pablo Fernandez22:08:46

with {x Long} I get: java.lang.RuntimeException: Binding form is not vector: {x Long}, compiling:(routes/services.clj:20:1)

Pablo Fernandez22:08:34

:body-params [{x :- Long nil}, {y :- Long nil}] might be a way, but it looks odd.

arohner22:08:55

I haven’t used compojure-api

arohner22:08:09

what does your fn look like?

arohner22:08:46

specifically, what does the fn binding look like?

arohner22:08:52

and what does “not work” mean?

Pablo Fernandez22:08:00

There’s no fn yet, I’m just building the playing with the API. I think you are thinking in terms of compojure, not compojure-api/swagger/schema