Fork me on GitHub
#core-async
<
2023-02-07
>
Mathieu Pasquet10:02:50

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....
  ; done

bortexz15:02:37

I’m using vscode and on the first example it prints done on the terminal, while the second example prints done on the REPL output

bortexz15:02:54

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

Mathieu Pasquet15:02:40

Hmmm, I stupidly didn't think to check the terminal :face_palm: What initially threw me off is the debugger. If you run if for f, it behaves really strangely.

Mathieu Pasquet15:02:02

The thread binding explanation sounds plausible

hiredman17:02:00

you should pass a callback to put! too

hiredman18:02:21

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