Fork me on GitHub
#core-async
<
2021-03-01
>
raspasov11:03:21

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.

Ben Sless15:03:12

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

raspasov15:03:07

@UK0810AQ2 thanks for taking a look at the source.

🙂 3
raspasov15:03:03

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

raspasov15:03:58

Good point on tools.logging, I haven’t used it, but I’ll take a look.

Ben Sless15:03:21

Feel free to steal ideas from https://github.com/halgari/com.tbaldridge.hermod regarding implementing a global registry, as well

👍 3
raspasov15:03:53

I have seen this library a long time ago, but I forgot about it 🙂 Will take a look again.

Ben Sless15:03:15

It's a very cool library, too bad it never took off

raspasov15:03:47

Is it basically trying to do what… Erlang is?

raspasov15:03:06

I’ve never used Erlang, only have some theoretical knowledge of how it works.

Ben Sless15:03:09

It's not exactly in Erlang-land. Erlang brings with it a whole framework and model https://github.com/suprematic/otplike

Ben Sless15:03:19

It's somewhere in the middle, I'd wager

raspasov15:03:51

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 🙂

Ben Sless15:03:53

It's worth to take a weekend to read the Armstrong thesis

👆 3
jjttjj16:03:17

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

Ben Sless19:03:11

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?

jjttjj20:03:18

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