beginners

Getting started with Clojure/ClojureScript? Welcome! Also try: https://ask.clojure.org. Check out resources at https://gist.github.com/yogthos/be323be0361c589570a6da4ccc85f58f.
Jack Arrington 2020-09-09T12:19:57.498500Z

Is it normal to define multimethods like this (defmulti my-fn #(mapv type %)) to achieve a sort-of function overloading in Clojure?

Jim Newton 2020-09-09T13:55:54.000500Z

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.

Jim Newton 2020-09-15T12:11:20.227300Z

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.

Jim Newton 2020-09-15T12:15:32.227700Z

If programmers should always use (sec ...) in place of (not (empty? ...)), then the compiler should do that for me.

2020-09-09T13:59:40.000600Z

(count (sequence (comp (map odd?) (filter identity)) [1 2 1 3 -4 -5]))
transducers should not create intermediate sequences

Jim Newton 2020-09-09T14:09:34.001200Z

🀯

pithyless 2020-09-09T14:09:39.001400Z

@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.

Jim Newton 2020-09-09T14:11:20.001600Z

In this case, I only want to know whether count is > 1. I guess I could write my own with reduce/reduced

Jim Newton 2020-09-09T14:13:03.001800Z

or perhaps (not (empty? (rest (filter odd? ...)))) BTW odd? is just an example, in my case the predicate is more costly.

pithyless 2020-09-09T14:13:24.002Z

(->> (filter odd? [1 2 1 3 -4 -5])
     (drop 1)
     (some?))

Jim Newton 2020-09-09T14:14:57.002300Z

so I need the parens around (some?) ?

pithyless 2020-09-09T14:16:17.002500Z

nah, that's just my convention; it's actually (->> (filter ..) (drop 1) seq some?) or perhaps (->> (filter ..) (drop 1) empty?)

pithyless 2020-09-09T14:16:44.002700Z

so, just checking if the seq has more than one item after being filtered

pithyless 2020-09-09T14:17:13.002900Z

hell, you can actually just check if (second (filter ...)) exists πŸ˜„

Jim Newton 2020-09-09T14:21:24.003100Z

πŸ™‚ transducer shmansducer

pithyless 2020-09-09T14:22:33.003300Z

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)) ;]

Jim Newton 2020-09-09T14:23:01.003500Z

πŸ‘

2020-09-09T14:32:39.003700Z

(second (filter ...)) this will not work if second element is nil )

βœ”οΈ 1
Jim Newton 2020-09-09T14:40:24.004200Z

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

Jim Newton 2020-09-09T14:41:31.004700Z

but I think there's an unintuitive abbreviation for` (not (empty? ...))`

Jim Newton 2020-09-09T14:48:52.005500Z

hey! nice resource https://clojure.org/reference/lisps

pithyless 2020-09-09T14:50:14.005600Z

There can be no nil, because it would have been filtered out

pithyless 2020-09-09T14:51:30.005800Z

There is also not-empty if you are so inclined; or next that will return nil only if the rest is an empty list.

pithyless 2020-09-09T14:52:56.006Z

> There can be noΒ `nil`, because it would have been filtered out ah right -- I didn't think of (pred nil) => something-truthy

dpsutton 2020-09-09T14:54:18.006200Z

there's both bounded-count and seq that can quickly tell you if the count is greater than 0

pithyless 2020-09-09T14:57:46.006400Z

(nil? (next (filter pred some-coll))) (< (bounded-count 2 (filter pred some-coll)) 2)

Jim Newton 2020-09-09T15:07:17.006600Z

> 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

Jim Newton 2020-09-09T15:07:48.006800Z

bounded-count, that's cool.

alexmiller 2020-09-09T15:19:09.007200Z

that is an ancient page :)

baptiste-from-paris 2020-09-09T16:29:11.008Z

can you be more specific ?

sova-soars-the-sora 2020-09-09T16:44:35.008200Z

are there any clojure-based analytics for web servers?

dharrigan 2020-09-09T20:40:16.012200Z

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>

dharrigan 2020-09-09T20:40:39.012700Z

In particular, it's this sentence, near the end that I can't seem to understand...

dharrigan 2020-09-09T20:40:52.012900Z

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.

dharrigan 2020-09-09T20:41:16.013300Z

I'm trying this out in a REPL and I observe the behaviour as presented in the post

dharrigan 2020-09-09T20:41:43.013700Z

I just don't understand however, what is meant the root value has already been reset back to 0, causing a race condition.

dharrigan 2020-09-09T20:42:07.014100Z

what is setting the value back to 42 and why a race condition?

dharrigan 2020-09-09T20:45:41.015Z

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.

Malik Kennedy 2020-09-09T20:46:33.015100Z

I think (seq x) is the better way to do (not (empty? x)) https://clojuredocs.org/clojure.core/empty_q

seancorfield 2020-09-09T20:49:59.015800Z

What if the future executed more quickly, before the with-redefs completes? That's your race condition.

dharrigan 2020-09-09T20:50:49.016Z

Is that what is happening here?

dharrigan 2020-09-09T20:51:27.016700Z

The with-redefs in this example doesn't seem to be doing anything much, it's wrapping a future.

seancorfield 2020-09-09T20:55:01.017600Z

user=&gt; (defn foo [] 0)
#'user/foo
user=&gt; (with-redefs [foo (fn [] 42)] (future (println (foo))) (dorun (range 1)))
nil
0
user=&gt; (with-redefs [foo (fn [] 42)] (future (println (foo))) (dorun (range 1)))
42
nil
user=&gt; 
The same code with different results.

jsn 2020-09-09T20:56:02.018600Z

@dharrigan after with-redefs completes, the value is reset back to what it was before with-redefs

dharrigan 2020-09-09T21:00:40.019Z

@jason358 thanks let me get back to you on that in a second

dharrigan 2020-09-09T21:00:51.019300Z

@seancorfield am I missing something as foo is undefined if I try this on my repl

seancorfield 2020-09-09T21:01:42.019800Z

Oh, sorry (defn foo [] 0) precedes that.

seancorfield 2020-09-09T21:02:05.020300Z

(but foo could be any function -- with-redefs is the issue here)

seancorfield 2020-09-09T21:04:55.020900Z

I did another run of it and updated the REPL session shown above.

dharrigan 2020-09-09T21:05:54.021600Z

I must have a faster machine πŸ™‚ I have to bump the range up to nearly 1000 to have differing results

dharrigan 2020-09-09T21:06:23.022Z

let me digest what has been said so far πŸ™‚

dharrigan 2020-09-09T21:09:09.022600Z

coming back to the article for a second

dharrigan 2020-09-09T21:09:48.023300Z

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.

dpsutton 2020-09-09T21:14:38.024300Z

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

dpsutton 2020-09-09T21:15:17.025100Z

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

seancorfield 2020-09-09T21:16:06.026Z

And the root value is reset back to 0 after with-redefs completes. It is not reset back to 42.

seancorfield 2020-09-09T21:16:50.027200Z

with-redefs temporarily sets it to (return, in my case) 42 -- and then resets it back to (return) 0.

jsn 2020-09-09T21:16:57.027400Z

@seancorfield hmm, now I'm confused too. Why? it's def 'ed as 42, so "back" should be 42

jsn 2020-09-09T21:17:12.027800Z

it's with-redef 'ed to 0, not 42

seancorfield 2020-09-09T21:17:18.028Z

I'm basing it on my code shown above.

seancorfield 2020-09-09T21:17:33.028500Z

Is that not the order the article does it? Let me look...

jsn 2020-09-09T21:18:05.029300Z

@dharrigan yeah, I think you've found a typo or something

seancorfield 2020-09-09T21:18:51.029900Z

Ah, right, the article starts out with 42 and does binding to 0. My bad. Sorry.

dharrigan 2020-09-09T21:20:07.031Z

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.

seancorfield 2020-09-09T21:20:10.031300Z

I assumed, based on the quote, it was doing what I showed. But, yeah, the article should say "reset back to 42".

dharrigan 2020-09-09T21:20:17.031700Z

(please say yes)

seancorfield 2020-09-09T21:20:22.032Z

I thought your confusion was something else πŸ™‚

jsn 2020-09-09T21:20:22.032100Z

@dharrigan yeah, that's my understanding

dharrigan 2020-09-09T21:20:30.032300Z

Now it all completely clicks

seancorfield 2020-09-09T21:20:57.033Z

(I mistakenly assumed the article was correct and you were just confused about why there was a race condition πŸ™‚ )

dharrigan 2020-09-09T21:21:14.033200Z

> 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.

dharrigan 2020-09-09T21:21:21.033400Z

I mentioned it above πŸ™‚

dharrigan 2020-09-09T21:21:36.033700Z

Anyhoooo πŸ™‚ phew

seancorfield 2020-09-09T21:21:56.034100Z

I guess I was confused about your confusion! We're all good now.

seancorfield 2020-09-09T21:22:28.035200Z

TL;DR: with-redefs is gross (but it is also still very useful if you know you won't have any race conditions)

dharrigan 2020-09-09T21:23:06.035900Z

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

seancorfield 2020-09-09T21:23:52.036600Z

Well, it may or may not have been reset back. Hence you may get zero or you may get forty-two.

dharrigan 2020-09-09T21:24:12.037100Z

Yes, the race condition stands true - it's dangerous territory πŸ™‚

seancorfield 2020-09-09T21:24:20.037500Z

(that's what I was trying to illustrate with my REPL session)

dharrigan 2020-09-09T21:24:51.038300Z

Yes, and I appreciate it - it helped to futher solidify my understanding and help figure out that the article was a little misleading πŸ™‚

dharrigan 2020-09-09T21:25:10.038700Z

Thank you @seancorfield, @jason358 and @dpsutton πŸ™‚

seancorfield 2020-09-09T21:26:16.040100Z

Just to prove it can happen with a simple Var, set to 42 initially and with-redefs'd to 0:

user=&gt; (def answer 42)
#'user/answer
...
user=&gt; (with-redefs [answer 0] (future (println answer)) (dorun (range 1)))
nil
42
user=&gt; (with-redefs [answer 0] (future (println answer)) (dorun (range 1)))
0
nil
user=&gt; 
Took me about half a dozen repetitions to see the value flip.

dharrigan 2020-09-09T21:26:53.040700Z

Would it be impolite to reach out to Tim to ask him to review the material and look to update the post?

dharrigan 2020-09-09T21:27:19.041400Z

Others may stumble across it (or be linked to it, from a discussion in another channel) and end up being confused too πŸ™‚

seancorfield 2020-09-09T21:27:25.041600Z

I think that would be a good idea!

dharrigan 2020-09-09T21:48:58.042500Z

Thank you everyone. I've reached out to Tim with the hope he might consider the change to the blog post πŸ™‚

dharrigan 2020-09-09T21:49:06.042800Z

ttfn!

alexmiller 2020-09-09T22:16:23.043300Z

He's not at Cognitect anymore so he's not going to be able to do that :)

alexmiller 2020-09-09T22:16:55.043800Z

I didn't actually read all that backchat, but if you could summarize what needs to be updated, I can probably do so

jsn 2020-09-09T22:32:39.045200Z

@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".