I'm confused about why something I'm doing fails with (take!) but works fine with (go (<! ...)).
Here's what I'm doing:
(require '[clojure.core.async :refer [go put! take! chan]])
; f is a function that will call itself asyncronously. In real usage, the put!
; is happening else-where and we want to free up the thread f is running on while we
; wait for that put.
; I know that this example can be represented as a loop rather than as a recursion,
; that's not the point.
; If you run this, it will never print "done."
(defn f [v]
(if (< v 100)
(let [c (chan)]
(put! c (inc v))
(take! c f false))
(println "done")))
(f 0)
; => nil
; If you re-write f as follows however, it works. I want to understand what the difference
; between the two is:
(defn f' [v]
(if (< v 100)
(let [c (chan)]
(put! c (inc v))
(go (f' (<! c))))
(println "done")))
(f' 0)
; => clojure.core.async.impl....
; doneI’m using vscode and on the first example it prints done on the terminal, while the second example prints done on the REPL output
I think it might be how the go is being dispatched to the executor, that captures thread bindings (potentially the out ?) , in contrast to how the take! callback is dispatched (`(dispatch/run #(fn1 val))`).
Hmmm, I stupidly didn't think to check the terminal 🤦 What initially threw me off is the debugger. If you run if for f, it behaves really strangely.
The thread binding explanation sounds plausible
you should pass a callback to put! too
(that will break things here because the channel is unbuffered so the callback to put! won't be called so the take inside the call back won't run, etc)