Fork me on GitHub
Mark Bailey04:10:14

hey all! anyone up to help with a question?


Sure, ask away!


(9pm on a Sunday night is kind of a quiet time @mbbailey96 🙂 )


Looks like you're East Coast?

Mark Bailey05:10:29

Ha yes! I'll be back on my computer shortly!


So the next thing I’m trying to figure out for my silly MUD is how to deal with updating atoms and managing side effects without hitting race conditions. For instance (forgive the pseduo-ish code)

(if (move-valid? curr-location direction)
    ; imagine prints being socket writes to the player
    (println "Moving " direction)
    ; This swap may fail because another thread may have changed
    ; the game state, and not move-valid? may be false
    (swap! game-state move player direction))
    ; I suppose I could compare the returned game-state of swap!
    ; with the original game-state, but would this possibly also
    ; be a race condition since the atom can be updated by a thread
    ; at any time?
  (println "can't move that way"))
My intuition is to fallback to manual locking or just serializing access to the atom. Am not sure what else to try 🤷


Are you planning for your application to have multiple threads running?


If not, then no worries.


Even if you are, you could write a pure function that combines the move-valid? and move functions into one, with no change to the position if the move is invalid. It could return what messages should be printed (or written to a socket, or in general any side effects), instead of actually doing the side effects, because swap! functions can be retried multiple times.


How weird would it be to push the player message queues into the atom itself such that appending messages to send to the players socket is now part of the swap! function?


I do not know if that would be weird, but thought of the same idea while drifting off to sleep last night. It is not clear to me what a better correct way to do it would be.


swap-vals! returns both the value of the atom before the call, and after, which could be helpful in having the caller make the desired side effects occur after the swap-vals! call returns.


Oh, never mind, swap! returns the after-value only, so swap-vals! does not seem helpful here.


After thinking about it, I'm thinking the solution may be: * Create a single queue within the atom to hold event objects, that gets appended during swap! that describe what takes place, or what prevents something from taking place. * Attach a watcher to the atom that processes the events in the queue and clears it once done. If you consider the case of a player moving from one room to the next, you actually have to notify all the players in the old room, all the players in the new room, and the player him/herself. Thus encoding the move into a single object, instead of trying to multiplex out all of those messages to all of the players before or after the swap! seems like the right thing here. Since add-watch gives the old state and new, this should allow for a consistent view of the world while processing the event.


Single threaded is an option, but I was hoping to make it multi-threaded for funsies if possible. What you are describing with having move call move-valid? is exactly the road I went down, and move either returns a modified or un-modified game-state depending. That’s what the comments below are referring to w.r.t checking if game-state had changed via swap! or not and taking some action depending. I like the idea of it returning messages to write to the socket (that whole idea of side-effects as data thing being awesome), but I didn’t think it possible for move to return both a game-state and messages since swap! is expecting a function to return game-state and nothing else.


hello, i have the following:

(defn testing-fn []
  (println "test!"))
(def a {:attrs {:exec "testing-fn"}})` evaluating ((resolve (symbol (:exec (:attrs a))))) works, however if i attempt this as part of a listener in the following frame
(def example2
  (frame :minimum-size [100 :by 100]
         :content (mig-panel :constraints ["" "" ""]
                             :items [[(button :text "test"
                                              :listen [:action (fn testfn [e] (let [t (:exec (:attrs a))]
                                                                                ((resolve (symbol t)))))])
                                      "sizegroup bttn, tag ok, push"]])))
it gives a nullpointerexception..Why can't it resolve the function in this context? Is there a way to actually achieve this?

Abhinav Sharma10:10:12

Hello Clojurians, I’m trying to find a recursive solution for this problem but struggling to think correctly for this one. I think that a combination of reduce-kv and recursion should do the trick but not able to formulate it concretely

{[:a 1 :X]    [[:e 1] [:f 1]]
[:e 1 :X]     [:t 1]
[:f 1 :X]     [:g 1]
[:g 1 :X]     [:h 1]
[:h 1 :k]    [:i 1] }
If the map key has :X in it then store it’s value and also check if that value also has a key with :X as well and collect these. For example, [:f 1 :X] -> [:g 1] => [:g 1 :X] -> [:h 1] then I should be able to accumulate the [:f 1] and [:g 1] in the function. The base condition, is that the [:h 1 :X] does not exist. The expected output of this function is a vector
[[:a 1]
[:e 1]
[:f 1]
[:g 1]]
Would be great if someone could help me out with this one.


Can't understand why [:h 1] is part of your output here.

Abhinav Sharma11:10:20

Oh, yeah - let me update this.

👍 4

By the logic you've described, [:h 1 :X] does not exist in the map, and so shouldn't be part of the output right?

👍 4

And the same with [:e 1] as well


Or did I misunderstand what you want to do?

Abhinav Sharma11:10:58

Hmm, well I was trying to create a minimal case - messed up the example a bit. Good observation @U883WCP5Z

Abhinav Sharma11:10:47

Yup, updated that

Abhinav Sharma11:10:31

I do think it’s a lot like linked lists but can’t correlate the concepts with recursion or reduce here.


Do you want something along the lines of "get me all the keys with :X in the third position, and the idents that can be reached from those"?

Abhinav Sharma11:10:55

Yup, almost there - the recursion part is “if those idents have :X themselves ( in their own key) then check their tree as well”

Abhinav Sharma12:10:13

So, let me know the context is missing.


Given this description, you don't need to traverse the graph. You can just do with a filter


Like this (mapv (comp pop key) (filter #(= :X (nth (key %) 2)) m)) where m is your map


because any key with :X in it you can reach in this filter, they don't need to be traversed from some other node.


That being said, I haven't run this code. On the phone

Abhinav Sharma14:10:57

Hmm, let me build on this one. 👍


This probably has a really obvious answer but instead of nested ifs what other options are there for ensuring that I don’t fetch availability and prices if the venue info couldn’t be found (incorrect id)

(defn booking-page-req-handler
  (let [info (get-venue-info venue-id)
        availability (get-venue-availability venue-id)
        prices (get-venue-prices venue-id)]
    (booking-page-html info availability prices)))


@matt.henley (when-let [info (get-venue-info venue-id)] (let [availability ...] ...))


when-let gives nil if the venue-id isn't found, you could use if-let to get a custom alternative value of course


So I have this ns form, but when I try to lein run I get this error.

Syntax error macroexpanding clojure.core/ns at (coding_test/calculator/routes.clj:1:1).
((:require [coding-test-calculator.logic :refer [balanced-parens? calculate illegal-char?] compojure.core :refer [defroutes POST] compojure.route :refer [not-found] ring.middleware.json :refer [wrap-json-body wrap-json-response]])) - failed: Extra input spec: :clojure.core.specs.alpha/ns-form
Which is weird cause the ns form looks correct. Here is the full stacktrace:


compojure.core is erroneously inside the vector for coding-test-calculator.logic


ah, I see. ty


each namespace should have its own vector, directly under :require

Brandon Olivier22:10:57

I'm having issues getting the clj/cljs repl switching working with shadow-cljs

Brandon Olivier22:10:56

I found on the docs it says to include shadow.cljs.devtools.server.nrepl04/middleware inside :repl-options, but lein throws an error trying to start the repl that that value doesn't exist


try shadow.cljs.devtools.server.nrepl/middleware. I just updated the docs an hour ago or so

Brandon Olivier22:10:00

when I checked out the namespace in a regular clojure repl, that var doesn't show up with ns-publics

Brandon Olivier22:10:53

I'm still getting Unable to resolve var: shadow.cljs.devtools.server.nrepl/middleware in this context

Brandon Olivier23:10:51

ah, the issue was the version. I upgraded to the latest, and it's working perfect now, thanks!