Fork me on GitHub
#core-async
<
2017-06-02
>
andershessellund09:06:40

I am wondering why core.async does not provide some functionality similar to async/await in C# and JS. go and <! provides a great foundation, but would it not be a good idea to have something that propagates exceptions as well?

mpenet09:06:03

they are just values ultimately, it's up to you

hlship16:06:32

Exceptions are always tricky to handle inside core.async. The growing consensus seems to be: catch the exception and pass it through the channel. When possible, if I have a "top level" I might pass a channel just for exceptions around, and the top-level will async/alts! the real result or the exception.

hiredman16:06:45

I really don't like it when I see exceptions being passed on the same channel as values

hiredman16:06:32

I suspect that split divides people on if they put loops in their go-blocks or not

hiredman16:06:48

if the go blocks are "long running" internal async services you communicate with via channels, or if go blocks are a calling convention for asynchronous functions

hiredman17:06:47

I like to give go blocks the equivalent of the std file descriptors, an in channel, out channel, and error channel (and maybe a shutdown channel)

hiredman17:06:37

but, given how often I see exceptions going on the same channel as values, and people writing wrappers for taking from channels that re-throw exceptions and that sort of thing, I suspect the async function style like what C# is the more common idiom

hlship17:06:57

@hiredman I think that's a good plan, and when I'm green-fielding code, that's a great approach. But I also have a lot of legacy core.async code to work on where my options are more limited.

peeja19:06:56

Is there an easier / more canonical way to transform values coming through a single channel than (async/map a-function [a-chan])?

peeja19:06:26

It feels weird to give it that single-element collection of the channel

noisesmith19:06:51

you can supply a transducing argument to the channel

noisesmith19:06:55

(when creating it)

noisesmith19:06:18

(async/chan N (map f))

noisesmith19:06:54

if you can’t make the channel yourself, you can use pipe and put the transduce on the to channel

noisesmith19:06:41

(async/pipe c (async/chan N (map f)))

noisesmith19:06:37

@peeja

peregrine.circle=> (require '[clojure.core.async :as >])
nil
peregrine.circle=> (def c (>/chan 4))
#'peregrine.circle/c
peregrine.circle=> (def d (>/chan 4 (map #(doto % println))))
#'peregrine.circle/d
peregrine.circle=> (def p (>/pipe c d))
#'peregrine.circle/p
peregrine.circle=> (= p d)
true
peregrine.circle=> (>/put! c "hello")
true
hello
peregrine.circle=> (>/put! c "world!")
world!
true

peeja19:06:29

Yeah, that makes sense. (async/pipe c (async/chan N (map f))) seems even clumsier than (async/map f [c]), though.

noisesmith19:06:48

well, you don’t need to pipe if you create the chan yourself

peeja19:06:04

Sure, but I'm not 🙂

peeja19:06:10

But I guess that makes sense

peeja19:06:33

I'm not sure I understand when you'd be in a position to create the channel yourself

peeja19:06:52

I guess some APIs let you create a channel and pass it into something which will put! to it?

noisesmith19:06:54

it’s good practice to take a chan as an arg, if you use one

noisesmith19:06:03

especially now that we have transducers and many buffer types

peeja19:06:25

That seems like it makes life even clumsier, though

peeja19:06:38

Maybe it's a matter of use cases

peeja19:06:45

I'm working with an HTTP API

peeja19:06:08

Perhaps naively, it seems easiest to me to have a function which returns a channel which will have the data

noisesmith19:06:17

a function that takes a chan as an arg, and promises to write to it, is much more reasonable to me than a function that returns the channel it will write to

ghadi19:06:43

FYI async/map and all of those kinds of functions are deprecated.

ghadi19:06:01

(in favor of adding a transducer to a channel.)

peeja19:06:48

It just reads so much more simply to (<! (make-an-http-call)) than to (let [c (chan)] (make-an-http-call c) c)

peeja19:06:04

Am I missing a way to twist that around?

noisesmith19:06:23

a good convention is to take the chan as an arg, and also return it - you can also make the arg optional

noisesmith19:06:31

that’s how pipeline does it (returns the to chan)

peeja19:06:40

Ah, interesting

peeja19:06:55

So just (<! (make-an-http-call (chan)))

noisesmith19:06:07

that was the point of the (= p d) in my example

peeja19:06:17

Oh, gotcha

noisesmith19:06:21

but in your case, you would also put a transducer in there

peeja19:06:16

So, replace my async/map calls with async/pipes with chans with transducers, then?

noisesmith19:06:32

well, you don’t need pipe if you can create the chan, but yeah

peeja19:06:56

Oh, right.

peeja19:06:49

So, if async/map is deprecated (and I wish that was mentioned in the docs), is there a modern equivalent that handles multiple channels?

ghadi19:06:50

I usually prefer processes taking channels as an argument

peeja19:06:10

I do have one use case for (async/map f [c1 c2 c3])

ghadi19:06:10

despite the extra ceremony

peeja19:06:02

Specifically, I've fired several HTTP requests, and I'm now combining them into a single piece of data

ghadi19:06:17

Oh my bad, async/map isn't deprecated, I confused it with map< and map>

peeja19:06:28

Ah, good. I'll keep using it there, then

peeja19:06:06

Damn. Passing a channel means I can't memoize the function anymore…

noisesmith19:06:27

wait, if you memoized it you would keep getting the same channel back

noisesmith19:06:34

is that what you wanted?

peeja19:06:39

Yeah, it's a promise-chan

noisesmith19:06:32

so if it’s a promise-chan, you aren’t really mapping per se, but calling a function on the single value once present

peeja19:06:42

Yeah, that's right

noisesmith19:06:44

I guess that’s a kind of mapping

peeja19:06:58

Still not used to using transducers with channels: how do I pick a reasonable value for the buffer size?

noisesmith19:06:46

if you don’t usually supply a buffer arg, I think 1 would be a safe bet