Fork me on GitHub
#core-async
<
2017-04-14
>
Yehonathan Sharvit06:04:07

How can I create a channel with no buffer?

Yehonathan Sharvit06:04:23

I tried

(def c (chan))

(put! c "hello")

(go
  (println "c: " (<! c)))

Yehonathan Sharvit06:04:01

In this case, the channel should be empty, when the reader reads it

Yehonathan Sharvit06:04:15

But in reality, it prints c: hello

Yehonathan Sharvit06:04:10

What I want is to create a channel such that messages that are written while no reader reads the channel are dropped

bronsa11:04:17

you can use a dropping-buffer

bronsa12:04:30

altho I don't think you can use a size 0

joost-diepenmaat12:04:18

put! actually buffers too

joost-diepenmaat12:04:07

and you can use size 0, but then you have to use >! and other blocking/parking methods to put values in the chan

joost-diepenmaat12:04:36

put! has to use a buffer since it’s guaranteed to immediately return, even if the chan is full

dergutemoritz13:04:57

@joost-diepenmaat It doesn't work for me with a buffer size of 0 neither with dropping-buffer nor with sliding-buffer

dergutemoritz13:04:31

@viebel What you can do is something like:

(def c (chan))
(alts!! [[c "hello"]] :default false)

dergutemoritz13:04:14

That will immediately return if there are no pending takes on c, otherwise put "hello" into c

dergutemoritz13:04:02

In the former case it will return [false :default]

dergutemoritz13:04:22

In the latter [true c]

Yehonathan Sharvit13:04:19

Do you have any idea why it cannot be done in the straightforward way - using put!?

Yehonathan Sharvit13:04:17

(In cljs, there is no alts!!, so I have to use alts! - which must be inside a go block 😞

dergutemoritz13:04:18

Because there is no buffer available with that semantic

dergutemoritz13:04:39

I just thought of another way, though, that should work with put!

dergutemoritz13:04:41

If you put a mult in between it should work

dergutemoritz13:04:14

But then you have to tap and untap on the reading side everytime ... not ideal either I guess

dergutemoritz13:04:34

Well you can hide the (go (alts! ...)) behind a function, of course 🙂

Yehonathan Sharvit13:04:04

Would it make sense to create a buffer policy where there is no buffer but puts will never be blocked?

dergutemoritz13:04:02

Sure, you could roll your own buffer with those semantics

dergutemoritz13:04:08

But that's a bit trickier

Yehonathan Sharvit13:04:36

I don’t understand why this is not the semantics of (chan)?

Yehonathan Sharvit13:04:45

It should create a channel with no buffer!!!!

Yehonathan Sharvit13:04:17

Why (alts! [[c "hello"]] :default false) behaves differently than (put! c)?

Yehonathan Sharvit13:04:56

in the case of no pending takes

Yehonathan Sharvit13:04:12

To me it’s really confusing!!

joost-diepenmaat13:04:34

(chan) does create a channel with no buffer

joost-diepenmaat13:04:36

the problem is that put! never blocks and uses an internal buffer when the chan’s buffer is full or doesn’t exist

joost-diepenmaat13:04:42

so don’t use put!

joost-diepenmaat13:04:31

then the other problem is that the semantics of a chan without a buffer is that it parks/blocks until there is a pending take

joost-diepenmaat13:04:00

and you cannot create a buffer with size 0 (I think) that would convert it to something like “dropping”

joost-diepenmaat13:04:12

basically because that would mean you’d pretty much always would be dropping messages

joost-diepenmaat13:04:27

unless you manage to have more than one pending take on the chan at all times

joost-diepenmaat13:04:25

I’m not sure that a dropping buffer with size 0 makes sense but I may be just confused

Yehonathan Sharvit14:04:55

My use case is something like this: i want to send a :stop message on a command channel but the :stop should be executed only if an active process is listening to the channel

Yehonathan Sharvit14:04:49

If the message is buffered when no active process is listening to the channel, then when a process will connect to the channel (after a while) it will read the :stop message that was sent before

Yehonathan Sharvit14:04:12

This is why I thought about having a channel with no buffer

Yehonathan Sharvit14:04:51

from the doc of put! it’s not very clear that the messages are buffered even if the channel has no buffer

Yehonathan Sharvit14:04:21

It seems that @dergutemoritz ’s suggestion with alts!! or alts! is the best solution (actually the only one!)

dergutemoritz14:04:29

@viebel Is there only ever going to be at most a single active process or can there be more than one at the same time that should then all react to such a :stop message?

Yehonathan Sharvit14:04:47

never more than one!

dergutemoritz14:04:20

OK then. Otherwise you'd have to use the mult approach

Yehonathan Sharvit14:04:47

Could you elaborate @dergutemoritz about how will I solve my no-buffer issue with mult?

Yehonathan Sharvit14:04:02

(def c (chan))
(def mult-c (mult c))
(def cx (chan))

(tap mult-c cx)

(>!! c "hello")

(<!! cx)

dergutemoritz14:04:33

Yep, that should do the trick

Yehonathan Sharvit14:04:04

(<!! cx) returns “hello”

dergutemoritz14:04:09

While no channel is tapped to the mult, all messages will be dropped

dergutemoritz14:04:15

Right, because it's tapped

dergutemoritz14:04:45

So while your process is active, it should be tapped

dergutemoritz14:04:52

And when it's done, it should untap itself

Yehonathan Sharvit14:04:33

(def c (chan))
(def mult-c (mult c))
(def cx (chan))


(put! c "hello")
(tap mult-c cx)
(go (println "read: " (<! cx)))
(untap mult-c cx)

Yehonathan Sharvit14:04:58

This work exactly as I want

Yehonathan Sharvit14:04:58

By the way, I discovered something interesting

Yehonathan Sharvit14:04:03

(def c (chan))
(def mult-c (mult c))


(put! c "hello")
(go (println "read from c: " (<! c)))

Yehonathan Sharvit14:04:27

If the channel is multiplied, then put! doesn’t buffer

Yehonathan Sharvit14:04:48

even without tapping into another channel!

dergutemoritz14:04:48

Yeah, that's how mult is supposed to behave

dergutemoritz14:04:11

i.e. don't buffer messages when there are no taps

dergutemoritz14:04:39

Same is true for pub btw because it's built on top of mult

Yehonathan Sharvit14:04:20

But then I cannot read from the original channel at all!!!

Yehonathan Sharvit14:04:25

(def c (chan))
(def mult-c (mult c))

(go (println "read from c: " (<! c)))
(go (>! c "hello"))

Yehonathan Sharvit14:04:37

nothin is printed!

dergutemoritz14:04:03

Oh right, sorry, I didn't read that closely enough

dergutemoritz14:04:25

Yeah, the input port of a mult is kind of bound to it

dergutemoritz14:04:43

You always have to go through tap if you want to read from it

dergutemoritz14:04:04

The docs are not very explicit about it but it's the only way it can work if you think about it

Yehonathan Sharvit14:04:45

otherwise one reader from the original channel will be able to “steal” the messages from the multiplied channel

dergutemoritz14:04:13

Yeah, it would result in pretty unpredictable behavior

dergutemoritz14:04:33

In fact, maybe you can even do that

dergutemoritz14:04:50

But that would be pretty confusing 😄