code-reviews 2021-11-09

@stopachka Have you considered using core.async for this?

IMHO, it would be def easier to achieve correct results, with a small runtime perf cost (if any); one downside is increased compilation times, because the more (go …) blocks you have, the longer it takes for the project to compile from scratch (which is not a big deal, since we don’t compile from scratch thanks to the to REPL, etc); on the positive side, the code can be basically the same between Clojure and ClojureScript (except in rare cases) which is something I value a lot;

❤️ 1

Writing correct JVM concurrent code is not for the faint of heart 🎩 😜 Even with core.async I’ve gotten into “what the hell moments” but definitely easier to reason about, IMO

❤️ 1

Thanks @raspasov! Yeah, I do think best bet would be core.async! Noting to try : }

👌 1

@emccue care to elaborate? I understand (go …) blocks have gotchas and some known bugs/issues, but in many use cases they work pretty well

https://openjdk.java.net/projects/loom/ This, basically. Its a guess, but i don’t think many projects will “outgrow” regular thread pools before this is available in however many years. The downsides of go blocks macroness wouldn’t be worth it

Got it. I am superficially aware of Project Loom but I’m not sure what the implications are for core.async in the long term. In terms of just core.async, on the JVM side you can always fall back to (thread …) instead of (go …) and using blocking (<!! …) instead of (<! …) ; That would be the only code change required I believe.

That way, you can get the benefits of channel semantics without using the (go …) macro, at the “expense” of using regular threads (which I completely agree are more than fine for most projects/problems).

That being said, I do find the (go …) macro useful in ClojureScript for a variety of situations. I try to reduce the amount of code inside a (go …) block to the bare minimum required. It decreases the likelihood you run into obscure issues.

Maybe i’m missing some core enlightenment, but i am perfectly happy with promesa for most of what i would want to do on the frontend. Promises are just one-shot channels eod

@emccue I completely agree promises can be fine for a variety of cases. But whenever you need, for example, strict ordering of events or some sufficiently complex animation logic, chan and things like alts! become useful; Say you need to have an animation that proceeds through stages but also needs to be interruptable by the user; A pseudo-code animation example:

(a/go
 (let [interrupt-ch (a/promise-chan)     ;this channel receives a value if the user interrupts the animation
       wait-ms      1000])
 (animate-something-1!)                  ;animate for 1 second
 (a/alts! [interrupt-ch (a/timeout wait-ms)]) ;interrupt?
 
 (animate-something-2!) ;animate again for 1 sec...
 (a/alts! [interrupt-ch (a/timeout wait-ms)]) ;interrupt?
 
 (animate-something-3!)                   ;and again...
 (a/alts! [interrupt-ch (a/timeout wait-ms)]) ;interrupt?
  )
Ofc you can achieve the same results via promises and some state, it’s just a question of using a higher level primitive to solve the problem.

core.asyncs channels, sure, but for a “forward-looking” project dont focus on go blocks