4

I am trying to figure out core.async in my REPL and am completely confused as to how my usage of (go-loop ...) doesn't manage to qualify as a "go block" for the purpose of async/>!

My go-loop is like...

(async/go-loop [page (range 3)]
  (if (empty? page)
    (async/close! ch)
    (dorun (map (fn [row]
                  (println row)
                  (async/>! ch row)) page)))
  (recur (range (dec (count page)))))

But the REPL is all upset...

=>
#object[clojure.core.async.impl.channels.ManyToManyChannel
        0x23465937
        "clojure.core.async.impl.channels.ManyToManyChannel@23465937"]
0
Exception in thread "async-dispatch-12" java.lang.AssertionError: Assert failed: >! used not in (go ...) block
nil
...

Why isn't the scope of that (go-loop ...) sufficient for the (async/>! row) call?

Should I even be using a go-loop here?

Bob Kuhar
  • 10,838
  • 11
  • 62
  • 115
  • Possible duplicate of [Clojurescript - Uncaught Error: <! used not in (go ...) block](https://stackoverflow.com/questions/32037795/clojurescript-uncaught-error-used-not-in-go-block) – OlegTheCat Sep 01 '17 at 10:34
  • @OlegTheCat The answer may be the same, but its not exactly the same question. Just like 2 + 5 = 7 and 8 - 1 = 7 aren't really the same question even though the answer is the same. Put another way, if one is intimately familiar with core.async this may look like the same question but to someone new...say googling for that exception message, you would not arrive at that Clojurescript question when looking for this exception message. – Bob Kuhar Sep 02 '17 at 01:24

1 Answers1

8

>! and other parking calls can't be used inside of functions nested inside of a go unfortunately.

go turns the code you give it into a state machine and looks for parking calls. It doesn't however look inside of nested functions.

From Clojure.Asyncs Github best practice page:

Unsupported constructs and other limitations in go blocks

The go macro stops translating at function creation boundaries. This means the following code will fail to compile, or may just throw a runtime error stating that <! was used outside of a go block:

(go (let [my-fn (fn [] (<! c))]
    (my-fn)))

This is one thing to remember since many Clojure constructs create functions inside macros. The following are examples of code that will not work as one would expect:

(go (map <! some-chan))
(go (for [x xs]
      (<! x)))
Community
  • 1
  • 1
Carcigenicate
  • 43,494
  • 9
  • 68
  • 117
  • so...```(do-seq...``` instead of the ```(dorun (map...``` attempt would get me out of my nested function anomaly? You may have guessed...I'm a little new to core.async. – Bob Kuhar Aug 31 '17 at 01:06
  • @BobKuhar Read the new update. Found an official source. Note that many macros produce functions, so that may not save you either. – Carcigenicate Aug 31 '17 at 01:07
  • 1
    Yep...```(doseq [row page] ... ``` overcomes the nested function thing and works really well. Thanks – Bob Kuhar Aug 31 '17 at 01:14
  • 1
    I moved the code blocks into the quoted block per your request for help. I think I did it right, but feel free to verify. For reference, you just needed to add `> ` (there's a trailing space there) to the beginning of the lines in the code block. – Nathan Davis Aug 31 '17 at 03:07
  • @NathanDavis Ahh. Thanks. That looks much better. – Carcigenicate Aug 31 '17 at 03:08