My confusion was on the OP's statement about "sigils": "if you use the wrong sigil at the wrong place, you are dead in the water."
So don't use the wrong sigil? There are all of two of them; I think OP means the parking take and blocking take macros. One is used inside go blocks and one outside. That was the easy part. The hard part was wrapping my head around how to efficiently program within the constraints imposed by core.async. But the machinery of how to do things (macros, functions) was very simple and easy to learn. You basically just need to learn "go", "<!" and "<!!". Eventually you may need ">!", "alts!", and "chan".
(defn test-dbg7 [] ;; test buffers
(record "test-dbg.svg"
(let [c ^{:name "chan"} (async-dbg/chan 1)]
^{:name "thread"}
(async-dbg/thread
(dotimes [n 3]
^{:name "put it!"} (async-dbg/>!! c n))
;; THE BUG IS HERE. FORGOT TO CLOSE GODAMNIT
#_(async-dbg/close! c))
(loop [x (async-dbg/<!! c)]
(when x
(println "-->" x)
(recur ^{:name "take it!"} (async-dbg/<!! c)))))))
The code above produces the following before hanging: --> 0
--> 1
--> 2
https://pasteboard.co/L4WjXavcFKaM.pngIn this test case, everything sits nicely within the same let statement, but these puts and reads to the same channel could be in different source files, making the bug hard to track.
Once the bug is corrected the sequence diagram should look like this:
The problem with core.async is that it is an excellent PoC, but does not actually solve the underlying problem, that is "Hey! I want a new thread here! And I want it cheap.". Project Loom solves it. Of course, the problem is not something that could be solved within the land of bytecode.