Fork me on GitHub
#core-async
<
2018-11-21
>
Garrett Hopper17:11:32

Is there a way to get a channel from alts! without having to create it myself? This pattern of creating a channel, starting a go-loop which puts onto that channel, and returning that channel doesn't seem very clean.

(let [chan (a/chan)]
  (a/go-loop []
    (let [[v c] (a/alts! chans)]
      (a/>! chan [v c]))
    (recur))
  chan)
Is there a different pattern for this type of thing? I often make the mistake of not returning the channel and not realizing it for awhile, since the go-loop returns a channel itself.

noisesmith17:11:51

something you'll see many functions in core.async do is expect the result channel as an argument

noisesmith17:11:20

an advantage there is that you can provide a new buffering strategy or transducer-enabled channel as a client, without needing to change the function

noisesmith17:11:39

also it means you need to pass the channel in or the function call won't work :D

Garrett Hopper17:11:07

Oh, that's a good idea. Thanks 🙂

Garrett Hopper17:11:55

Or I guess a muli-arity function with an optional buf-or-n?

noisesmith17:11:10

that's also an option - but I also like the idea of a function returning the chan from a go-block if it starts one (though there's no rule saying a dog can't play basketball you can't return N channels)

Garrett Hopper18:11:35

@noisesmith, so you would return a vector with the go-loop chan and the chan that was provided or created?

hiredman18:11:35

why return the channel created by the go loop?

Garrett Hopper18:11:56

I guess so it can be closed by the user if need be?

hiredman18:11:06

closing it does nothing

Garrett Hopper18:11:27

Then I got nothing :man-shrugging::skin-tone-2:

noisesmith18:11:41

I like holding on to it, as it's a straightforward way to know if the loop has exited

Garrett Hopper18:11:34

Is this the only way to execute a function for every value on a channel?

(a/go-loop []
  (do-thing (a/<! chan$))
  (recur))
Using map only works if the returned channel is actually read from; how do I force it to evaluate for side-effects?

noisesmith18:11:35

one option is (async/transduce (map do-thing) some-op init chan$) where some-op is a reducing step - maybe a no-op and init is whatever some-op wants, maybe just nil

Garrett Hopper18:11:20

Thanks 🙂 I guess it's finally time for me to figure out what transducers are. 😛

noisesmith18:11:36

they are useful, many parts of Clojure can use them - in common cases you can replace (->> x (map f) (filter g)) with (comp (map f) (filter g)) and get the same behavior with better performance and composibility / first class values as it's a data transform not a syntax

noisesmith18:11:57

at @dayjob I use a transducer to define the data processing pipeline. In prod that's attached to kafka channels in and out, in tests it can just be hooked up to sequences and we know it's the same thing with the same behavior

noisesmith18:11:28

this avoids the gotchas of trying to pretend kafka itself provides a seq / consumes a seq