interesting new library. Any thoughts from here? https://clojurians.slack.com/archives/C06MAR553/p1614592215160400
Let me know if you have any questions about it. I’ve been using core.async for a quite a long time now. Been through multiple iterations of a pattern to manage go-loop’s on the REPL and in a live system (both in Clojure and ClojureScript). It does use a stateful atom inside the library. This currently seems like a decent compromise between simplicity and ease of use.
I'm not sure how it compares to constantly polling a stop channel, but since you're using the stop channel as a promise and just wait for stop and pass other signals, why not just use an AtomicBoolean? Might also want to remove the timbre dependency and allow the user to inject their own logger with tools.logging
@ben.sless thanks for taking a look at the source.
I would like it to be a Clojure(Script) library (in fact using it primarily from Cljs now); I guess AtomicBoolean can be used as an optimization on Clojure JVM
Good point on tools.logging, I haven’t used it, but I’ll take a look.
Feel free to steal ideas from https://github.com/halgari/com.tbaldridge.hermod regarding implementing a global registry, as well
I have seen this library a long time ago, but I forgot about it 🙂 Will take a look again.
It's a very cool library, too bad it never took off
Is it basically trying to do what… Erlang is?
I’ve never used Erlang, only have some theoretical knowledge of how it works.
It's not exactly in Erlang-land. Erlang brings with it a whole framework and model https://github.com/suprematic/otplike
It's somewhere in the middle, I'd wager
Right… I’ve heard (perhaps from Timothy Baldridge while watching his talks) the concept of Erlang described via the mailboxes analogy; basically in Erlang you’re somebody who can only communicate with others via message passing (as if you’re stuck in mailbox); That’s about the extent of my knowledge on the topic 🙂
It's worth to take a weekend to read the Armstrong thesis
I've been using a "derefable mult" lately, which is like core.async/mult
but can be deref'd for the most recent value. I've been in situations a lot where I want a dataflow graph that is essentially a DAG but then more complicated state machines at the leaves. I also have a UI layer where I want to just be able to get a best effort recent value to show. Any thoughts why this might be a bad or good idea?
(defn dmult
"like core.async mult, but can be deref'd for the most recent input value"
[ch]
(let [cs (atom {}) ;;ch->close?
prev (atom nil)
m (reify
a/Mux
(muxch* [_] ch)
a/Mult
(tap* [_ ch close?] (swap! cs assoc ch close?) nil)
(untap* [_ ch] (swap! cs dissoc ch) nil)
(untap-all* [_] (reset! cs {}) nil)
#?@(:clj
[clojure.lang.IDeref
(deref [this] @prev)]
:cljs
[IDeref
(-deref [this] @prev)]))
dchan (chan 1)
dctr (atom nil)
done (fn [_] (when (zero? (swap! dctr dec))
(a/put! dchan true)))]
(go-loop []
(let [val (<! ch)]
(if (nil? val)
(doseq [[c close?] @cs]
(when close? (a/close! c)))
(let [chs (keys @cs)]
(reset! dctr (count chs))
(doseq [c chs]
(when-not (a/put! c val done)
(a/untap* m c)))
;;wait for all
(when (seq chs)
(<! dchan))
(reset! prev val) ;;should this go before or after all puts?
(recur)))))
m))
The reason I can think of why it can be a problem is because it does two things, which is confusing and makes your code complected (heh). If we break it apart there are two components here: a regular mult and a latch. Why not just implement a latch and tap the mult with it?
I guess I just wanted this so often that I ended up having two things per logical thing which was getting harder to reason about