core-async

2021-03-01T11:18:54.012Z

interesting new library. Any thoughts from here? https://clojurians.slack.com/archives/C06MAR553/p1614592215160400

raspasov 2021-03-01T11:45:21.012200Z

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 Sless 2021-03-01T15:11:12.012400Z

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

raspasov 2021-03-01T15:30:07.012600Z

@ben.sless thanks for taking a look at the source.

🙂 1
raspasov 2021-03-01T15:31:03.012900Z

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

raspasov 2021-03-01T15:31:58.013200Z

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

Ben Sless 2021-03-01T15:33:21.013400Z

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

👍 1
raspasov 2021-03-01T15:34:53.013800Z

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

Ben Sless 2021-03-01T15:35:15.014Z

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

raspasov 2021-03-01T15:35:47.014200Z

Is it basically trying to do what… Erlang is?

raspasov 2021-03-01T15:36:06.014400Z

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

Ben Sless 2021-03-01T15:42:09.014700Z

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

Ben Sless 2021-03-01T15:42:19.015Z

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

raspasov 2021-03-01T15:44:51.015200Z

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 Sless 2021-03-01T15:50:53.015400Z

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

👆 1
2021-03-01T16:43:17.018500Z

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 Sless 2021-03-01T19:53:11.018900Z

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?

2021-03-01T20:23:18.019100Z

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