core-async

Mathieu Pasquet 2023-02-07T10:37:50.034269Z

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

bortexz 2023-02-07T15:08:37.294069Z

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

bortexz 2023-02-07T15:30:54.334309Z

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 Pasquet 2023-02-07T15:38:40.405389Z

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.

Mathieu Pasquet 2023-02-07T15:39:02.049799Z

The thread binding explanation sounds plausible

2023-02-07T17:58:00.577209Z

you should pass a callback to put! too

2023-02-07T18:02:21.505539Z

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