Is it normal to define multimethods like this (defmulti my-fn #(mapv type %))
to achieve a sort-of function overloading in Clojure?
what's a good way to count the number of times a given predicate function returns true when mapped over a sequence? E.g., (count-matches odd? [1 2 1 3 -4 -5])
should return 4. Perhaps (count (filter ???))
but there's really need to create a new sequence.
Yes I've had this discussion several times before. For me seq
does not express the intent of (not (empty? ...))
and in my opinion the compiler should compile them to the exact same instructions, but it fails to (or refuses to) do so.
If programmers should always use (sec ...)
in place of (not (empty? ...))
, then the compiler should do that for me.
(count (sequence (comp (map odd?) (filter identity)) [1 2 1 3 -4 -5]))
transducers should not create intermediate sequencesπ€―
@jimka.issy are you worried about the GC from doing?
(count (filter odd? [1 2 1 3 -4 -5]))
That seems like an unnecessary concern.In this case, I only want to know whether count is > 1. I guess I could write my own with reduce/reduced
or perhaps (not (empty? (rest (filter odd? ...))))
BTW odd? is just an example, in my case the predicate is more costly.
(->> (filter odd? [1 2 1 3 -4 -5])
(drop 1)
(some?))
so I need the parens around (some?)
?
nah, that's just my convention; it's actually (->> (filter ..) (drop 1) seq some?)
or perhaps (->> (filter ..) (drop 1) empty?)
so, just checking if the seq has more than one item after being filtered
hell, you can actually just check if (second (filter ...))
exists π
π transducer shmansducer
My original point still stands - don't be so worried about the performance of lazy-seqs; unless you can measure actual slow performance. In this case, you do probably just want (second (filter pred some-coll))
;]
π
(second (filter ...))
this will not work if second element is nil )
what about just (rest (filter pred some-coll))
but as I understand an empty collection is not false. That's why I said (not (empty? (rest (filter red some-cool))))
but I think there's an unintuitive abbreviation for` (not (empty? ...))`
hey! nice resource https://clojure.org/reference/lisps
There can be no nil
, because it would have been filtered out
There is also not-empty
if you are so inclined; or next
that will return nil
only if the rest is an empty list.
> There can be noΒ `nil`, because it would have been filtered out
ah right -- I didn't think of (pred nil) => something-truthy
there's both bounded-count
and seq
that can quickly tell you if the count is greater than 0
(nil? (next (filter pred some-coll)))
(< (bounded-count 2 (filter pred some-coll)) 2)
> There can be noΒ `nil`, because it would have been filtered out
I don't understand this claim. Of course I can have a predicate which returns true
when its argument is nil
among others
bounded-count
, that's cool.
that is an ancient page :)
can you be more specific ?
are there any clojure-based analytics for web servers?
Following on from a discussion in #clojure-uk, I'm trying to wrap my head around the information presented here <https://cognitect.com/blog/2016/9/15/works-on-my-machine-understanding-var-bindings-and-roots>
In particular, it's this sentence, near the end that I can't seem to understand...
Since Var roots are global for the entire application, once the body of the future gets around to deref-ing the var (after the 1 sec pause) the root value has already been reset back to 0, causing a race condition.
I'm trying this out in a REPL and I observe the behaviour as presented in the post
I just don't understand however, what is meant the root value has already been reset back to 0, causing a race condition.
what is setting the value back to 42 and why a race condition?
What is throwing me is this the root value has already been reset back
- it suggests to me that something behind the scenes is changing the value of the var back to the value of 42
.
I think (seq x) is the better way to do (not (empty? x)) https://clojuredocs.org/clojure.core/empty_q
What if the future executed more quickly, before the with-redefs
completes? That's your race condition.
Is that what is happening here?
The with-redefs in this example doesn't seem to be doing anything much, it's wrapping a future.
user=> (defn foo [] 0)
#'user/foo
user=> (with-redefs [foo (fn [] 42)] (future (println (foo))) (dorun (range 1)))
nil
0
user=> (with-redefs [foo (fn [] 42)] (future (println (foo))) (dorun (range 1)))
42
nil
user=>
The same code with different results.@dharrigan after with-redefs
completes, the value is reset back to what it was before with-redefs
@jason358 thanks let me get back to you on that in a second
@seancorfield am I missing something as foo is undefined if I try this on my repl
Oh, sorry (defn foo [] 0)
precedes that.
(but foo
could be any function -- with-redefs
is the issue here)
I did another run of it and updated the REPL session shown above.
I must have a faster machine π I have to bump the range up to nearly 1000 to have differing results
let me digest what has been said so far π
coming back to the article for a second
It would make sense to me, if the line was instead of the root value has already been reset back to 0, causing a race condition.
it was the root value has already been reset back to 42, causing a race condition.
the race is that you have code expecting the value to be 42 and code that will change the value back to 0. the order of those events is not deterministic and therefore you have a race
sometimes the future will execute in totality first, sometimes the resetting back to 0 will happen first, sometimes the future will execute some portion with 42, then some portion with 0. its a race
And the root value
is reset back to 0
after with-redefs
completes. It is not reset back to 42
.
with-redefs
temporarily sets it to (return, in my case) 42
-- and then resets it back to (return) 0
.
@seancorfield hmm, now I'm confused too. Why? it's def
'ed as 42, so "back" should be 42
it's with-redef
'ed to 0, not 42
I'm basing it on my code shown above.
Is that not the order the article does it? Let me look...
https://cognitect.com/blog/2016/9/15/works-on-my-machine-understanding-var-bindings-and-roots -- this is the code
@dharrigan yeah, I think you've found a typo or something
Ah, right, the article starts out with 42
and does binding
to 0
. My bad. Sorry.
Riiigh, so is it a typo then on that line? Should it be reset back to 42, causing a race condition.
not reset back to 0, causing a race condition.
I assumed, based on the quote, it was doing what I showed. But, yeah, the article should say "reset back to 42".
(please say yes)
I thought your confusion was something else π
@dharrigan yeah, that's my understanding
Now it all completely clicks
(I mistakenly assumed the article was correct and you were just confused about why there was a race condition π )
> What is throwing me is thisΒ the root value has already been reset backΒ - it suggests to me that something behind the scenes is changing the value of the var back to the value ofΒ 42.
I mentioned it above π
Anyhoooo π phew
I guess I was confused about your confusion! We're all good now.
TL;DR: with-redefs
is gross (but it is also still very useful if you know you won't have any race conditions)
Now I get it, the with-redefs sets the answer to be 0, but it modifies the root binding, only for the duration of that block. When the block exits, the value is put back to 42, in the meantime, the future inside that block will complete at some point and where you the programmer was thinking that the value of the answer would be 0
, it's now 42
because it's been reset back
Well, it may or may not have been reset back. Hence you may get zero or you may get forty-two.
Yes, the race condition stands true - it's dangerous territory π
(that's what I was trying to illustrate with my REPL session)
Yes, and I appreciate it - it helped to futher solidify my understanding and help figure out that the article was a little misleading π
Thank you @seancorfield, @jason358 and @dpsutton π
Just to prove it can happen with a simple Var, set to 42
initially and with-redefs
'd to 0
:
user=> (def answer 42)
#'user/answer
...
user=> (with-redefs [answer 0] (future (println answer)) (dorun (range 1)))
nil
42
user=> (with-redefs [answer 0] (future (println answer)) (dorun (range 1)))
0
nil
user=>
Took me about half a dozen repetitions to see the value flip.Would it be impolite to reach out to Tim to ask him to review the material and look to update the post?
Others may stumble across it (or be linked to it, from a discussion in another channel) and end up being confused too π
I think that would be a good idea!
Thank you everyone. I've reached out to Tim with the hope he might consider the change to the blog post π
ttfn!
He's not at Cognitect anymore so he's not going to be able to do that :)
I didn't actually read all that backchat, but if you could summarize what needs to be updated, I can probably do so
@alexmiller I think at least this one has to be fixed: in the root value has already been reset back to 0, causing a race condition
, "0" must be changed to "42".