This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-11-16
Channels
- # adventofcode (1)
- # announcements (16)
- # babashka (7)
- # beginners (77)
- # calva (31)
- # cider (18)
- # clj-commons (16)
- # cljfx (5)
- # clojars (5)
- # clojure (33)
- # clojure-europe (15)
- # clojure-nl (1)
- # clojure-norway (15)
- # clojure-uk (4)
- # clojurescript (1)
- # conjure (1)
- # core-logic (7)
- # cursive (16)
- # data-science (4)
- # datalevin (6)
- # emacs (20)
- # events (5)
- # fulcro (15)
- # holy-lambda (1)
- # introduce-yourself (1)
- # jobs (2)
- # lsp (30)
- # luminus (3)
- # malli (3)
- # membrane-term (19)
- # missionary (62)
- # off-topic (39)
- # pathom (24)
- # polylith (5)
- # portal (9)
- # practicalli (3)
- # re-frame (16)
- # reagent (5)
- # remote-jobs (1)
- # reveal (21)
- # rewrite-clj (8)
- # shadow-cljs (13)
- # spacemacs (23)
- # sql (12)
- # timbre (2)
- # tools-deps (1)
- # xtdb (4)
yes it's delightful learning with and from y'all 😄
idiom check: i have a map and new item to add to the map and a list of keys i want to attach the new item to:
=> map
{:a 1 :b 2}
=> item
3
=> keys
[:c :d :e]
=> new-map
{:a 1 :b 2 :c 3 :d 3 :e 3}
is this best accomplished with reduce?
(let [old-map {:a 1 :b 2}
item 3
ks [:c :d :e]
new-map (reduce
(fn [m k]
(assoc m k item))
old-map
ks)]
...)
there you go, thank you
i had a feeling something like that existed
(reduce (fn [m k] (assoc m k x)) m ks)
Is about the same length of characters, and more light weight
(reduce #(assoc %1 %2 x) m ks)
If you like this syntactic sugaryou know i do
yeah, i tend to stay away from the fn macro when there’s more than 1 input
My goal is to initialize an object only once when invoked by multiple threads.
I came up with this solution and it seems to be working fine. But I’d like to know if there are
any pitfalls I’m overseeing. Specifically worried about some threads getting nil
instead of
the instantiated object.
(def my-obj (atom nil))
(defn init-my-obj
[]
(or @my-obj
(do (compare-and-set! my-obj nil (SomeClass.))
@my-obj)))
@UMPJRJU9E I think there is a risk of multiple instantiations with your implementation if init-my-obj
is called on multiple threads. The reason is that the atom can be deref-ed as nil
on multiple threads before any of them have set the instance.
This explains why delay
is the correct approach: https://danlebrero.com/2017/06/12/delay-clojure-forgotten-concurrency-primitive/
any time you have something that derefs an atom to check the value followed by a mutation, you have a potential race
And this is also good reading material: https://purelyfunctional.tv/guide/clojure-concurrency/
not a potential race, an actual race :)
delay or defonce are two good tools for this. delay is better from an aot compilation point of view, but requires consumers to deref. defonce is easier for consumers but will be evaluated during aot, which you often don't want.
> any time you have something that derefs an atom to check the value followed by a mutation, you have a potential race
Also good to know about swap-vals!
if you do need both: https://clojuredocs.org/clojure.core/swap-vals!
often I combine def / delay with a wrapper function that does the deref for a friendlier combo
Sorry, I only posted partial code. I did think of delay but it won’t fit my case since I need to pass params while initalizing the class which might not always be the same
Can this be converted to a delay?
(def sem (atom nil))
(defn- max-instances-semaphore
[permits]
(or @sem
(do (compare-and-set! sem nil (Semaphore. permits))
@sem)))
if any threads have permits, then you call that replacing the semaphore object, and those threads don't lexically capture sem, when those threads release permits they will release on the new semaphore increasing the number of permits it has
Ironic, I know, but how do I go about doing something like this? I could always pass the instantiated semaphore to the threads but was thinking if there is construct for doing something like this
> if any threads have permits, then you call that replacing the semaphore object, and those threads don’t lexically capture sem, when those threads release permits they will release on the new semaphore increasing the number of permits it has Yeah, the goal is avoid this exactly
in general, I find it is usually better to avoid using something like a semaphore at all, and instead limit concurrency via something like a fixed size executor
The thing is we’re using integrant and I need to instantiate the Semaphore elsewhere and pass it to the function that will be invoked parallely. The function api would’ve been simpler if I could just pass the max permits instead of the actual Semaphore.
> in general, I find it is usually better to avoid using something like a semaphore at all, and instead limit concurrency via something like a fixed size executor Yup, totally agree, but in this special case, the objects go stale quite quickly (browser drivers) and a Semaphore seemed like a quick way to cap the objects.
have you considered actually using java.util.concurrent.Semaphore ?
or is that what you're using
generally, if you're having trouble def'ing something, maybe don't :) construct at startup and pass to those that need it
a fixed size executor will also let you adjust the number of threads doing work on the fly without creating a sequence of semaphores
(you can do that on a single semaphore as well but you may need some extra accounting)
> generally, if you’re having trouble def’ing something, maybe don’t :) construct at startup and pass to those that need it That’s what I was doing passing an instantiated Semaphore. But thought passing the max-instance-number would simplify the API than passing in a construct that would cap the number of objects (object pool, semaphore), but I guess it’s not worth it.
objects don't magically pop into existence, code has to create them, and that code has to run somewhere
another idiom check! i have an app-state
map atom, and i’ve taken to writing pairs of functions that operate on it, a do-X!
function that calls swap!
and a do-X-impl
that takes in the deref
’d map and returns the whole map with changes applied. I’m doing this because I need to keep data in multiple keys in sync (vs having a data race by deref
’ing and swap!
ing multiple times in a row). is this an okay means of handling this? here’s an example:
(defn ^:private register-game-impl
[{:keys [games users uid->gameid gameid->uids]} game]
(let [gameid (:gameid game)
games (assoc games gameid game)
uids (keep :uid (:players game))
uid->gameid (merge uid->gameid (zipmap uids (repeat gameid)))
gameid->uids (assoc gameid->uids gameid (set uids))]
{:games games
:users users
:uid->gameid uid->gameid
:gameid->uids gameid->uids}))
(defn register-game! [game]
(swap! app-state register-game-impl game))
@UEENNMX0T not directly related to your question, but I would push back on whether you need to be building and storing these caches/indexes in the atom. What would happen if state was just {:games ,,, :users ,,,}
and the indexes were generated and passed around in local bindings where they would actually be needed?
ie. are multiple unrelated "threads" operating on these indexes (so you have to coordinate them) AND are the indexes used so often that they are worth it to generate and cache upfront AND are the read vs write statistics skewed towards reading (i.e. if you're swap!-ing more often than deref-ing, this could in fact be a net-negative for performance).
single threaded, single jar running on a digital ocean box, maybe like 100 players at a given time playing less than 50 games (2 player card game) so impact is low either way
i did the caching to make certain “queries” easy for myself
my point was, if impact is low, and the caches are localized - and quick to generate - it may be helpful to not try to keep them up-to-date in the atom (which complicates the code for all updates of the atom)
you can have a function that generates uid->gameid
for easy queries without trying to keep it in sync when doing swaps
that makes sense. i certainly don’t know what i’m doing, i just know that the way the code is currently written is very bad lol (multiple atoms that each have to be updated individually, spaghetti code in general, etc)
cool, i’ll try that out
@UEENNMX0T BTW, your state map looks really similar to the kind of graph-like map-structures that Joinery and friends are trying to optimize for: https://github.com/cjsauer/joinery and https://github.com/cjsauer/joinery#prior-art - so if you're looking for a way to simplify your queries and mutations, perhaps you can use one of the ready-made libraries (or at least get some inspiration from their apis)
thanks for the link! i’ll check it out
i first had this as an inlined function to swap!
and then as a function in a let
block before pulling it all the way out into its own function for clarity.
yes, good
register-game-impl is then pure and you can test it, spec it, whatever and the state is isolated in register-game!
you might consider whether you even need register-game!. If you make your "transformation" functions all external, then you can sometimes keep the state local to the place where you create it and avoid even making stateful functions
cool, thanks
sometimes I'll play that game to try to minimize the number of places that even need to be aware there is state - can you reduce it to the point that all stateful code ends up on a single screen and you can see it together?
hah that’s an interesting idea! certainly seems like it could be doable if I can architect it right. right now, app-state
is an in-memory database because the game objects aren’t serializable (lots of stored functions) and the !
functions are being called from sente handlers (web socket messages) which makes returning a new app-state
a little tricky but not impossible
ah, maybe not possible then
but that's the premise I start from - consider state and stateful functions to be radioactive and apply pressure to minimize their spread
Unfortunately, I found that this can exert pressure which can escape via unexpected release valves, mainly falling into the trap pointed out in Out Of The Tar Pit of passing the entire world as an extra argument
I'm trying to set up a web app using the reagent template and updating it to use httpkit and sente.
I started with lein new reagent myproject +cider +figwheel
then followed https://github.com/ciwchris/websocket-example to replace jetty with http-kit
in order to set myself up with https://github.com/ptaoussanis/sente
https://github.com/jollyblondgiant/clj-pokedex; The project compiles as long as one doesn't actually try to require sente in https://github.com/jollyblondgiant/clj-pokedex/blob/master/src/clj/pokedex/handler.clj or https://github.com/jollyblondgiant/clj-pokedex/blob/master/src/cljs/pokedex/core.cljs
if I try to (:require [taoensso.sente :as sente :refer (cb-success?)])
in the client, compilation fails with No such namespace: cljs.tools.reader.edn
if i try to (:require [taoensso.sente :as sente])
in the handler, compilation breaks with
java.lang.RuntimeException: No such var: str/starts-with?
and if I try to (:require [taoensso.sente.server-adapters.http-kit
:refer (get-sch-adapter)])
(also in the handler), compilation breaks with:
java.lang.RuntimeException: No such var: hk/as-channel
each of these errors appears to be way above my application level, and I've removed reference to sente outside of the require statements. Am I using the right sente version? could there be some other setup missing in my environment?
@UQZQ9T3NV these deps are ancient:
[org.clojure/clojure "1.7.0-RC1"]
[org.clojure/clojurescript "0.0-3291" :scope "provided"]
I would start by bumping these deps, especially since this is a new projectcurrent Clojure is 1.10.3
and ClojureScript is 1.10.891
(or at least 1.10.773
as defined in sente deps)
I wonder if adding httpkit changed some transient dependency versions and you have some stale class files aot compiled from the old versions
https://github.com/jollyblondgiant/clj-pokedex/blob/master/project.clj - well, for starters, looks like a really old version of Clojure and ClojureScript
Here’s https://github.com/clojure/clojure/blob/1d271d7bc74f64831985f89be1d814df2c5f046f/src/clj/clojure/core.clj#L7626 for the some->
macro:
(defmacro some->
"When expr is not nil, threads it into the first form (via ->),
and when that result is not nil, through the next etc"
{:added "1.5"}
[expr & forms]
(let [g (gensym)
steps (map (fn [step] `(if (nil? ~g) nil (-> ~g ~step)))
forms)]
`(let [~g ~expr
~@(interleave (repeat g) (butlast steps))]
~(if (empty? steps)
g
(last steps)))))
My question is this:
I’m confused about the benefit to using butlast
and potentially saving a single binding. Why not go with a simpler body of:
`(let [~g ~expr
~@(interleave (repeat g) steps)]
~g)