Fork me on GitHub
#core-async
<
2016-10-19
>
tap01:10:48

I have a question about stopping go-loop which has alts! inside. Normally for go-loop which has <! inside, I do this

(def c (chan))
(go-loop []
  (let [m (<! c)]
    (when m
      (do-stuff m)
      (recur))))
(close! c)
But for one with alts! inside, I have to do this.
(def c1 (chan))
(def c2 (chan))
(go-loop [closed #{}]
  (when-not (= (count closed) 2)
    (let [[m c] (alts! [c1 c2])]
      (if (not= m :close)
        (do (do-stuff m)
            (recur closed))
        (recur (conj closed c))))))
(go (>! c1 :close))
(go (>! c2 :close))
Is there a less verbose way to achieve this?

hiredman02:10:17

well, for one, you can just use close!

hiredman02:10:33

m will be nil when 'c' is closed in alts!

hiredman02:10:51

you can change closed to be opened, and alts on it, and remove channels when they are closed, and exit the go loop when opened is empty

hiredman02:10:59

(def c1 (chan))
(def c2 (chan))
(go-loop [open [c1 c2]]
  (let [[m c] (alts! open)]
    (if m
      (do
        (do-stuff m)
        (recur open))
      (recur (remove #{c} open)))))
(go (close! c1))
(go (close! c2))

hiredman02:10:01

you could also use merge

hiredman02:10:53

I forgot to exit when open is empty above

tap02:10:55

The problem I found with this implementation is that alts! will always success with nil from that closed channel and it will keep looping for free until the other channel is closed.

tap02:10:27

It’s not one time nil thing per close!

tap02:10:21

Oh, I think I misunderstood it. Will give it a try

tap02:10:25

Sorry about that

tap02:10:41

The only problem I got from that implementation is (alts! []) throws ArrayIndexOutOfBoundsException. Other than that it looks great. Thanks

tap02:10:08

I think I’m ok with wrapping let block with (when (seq open) … ). It’s already a great improvement.