Fork me on GitHub
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")

  (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


you can use a dropping-buffer


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


put! actually buffers too


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


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


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


@viebel What you can do is something like:

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


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


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


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 😞


Because there is no buffer available with that semantic


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


If you put a mult in between it should work


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


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?


Sure, you could roll your own buffer with those semantics


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!!


(chan) does create a channel with no buffer


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


so don’t use put!


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


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


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


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


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!)


@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!


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)


Yep, that should do the trick

Yehonathan Sharvit14:04:04

(<!! cx) returns “hello”


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


Right, because it's tapped


So while your process is active, it should be tapped


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!


Yeah, that's how mult is supposed to behave


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


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!


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


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


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


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


Yeah, it would result in pretty unpredictable behavior


In fact, maybe you can even do that


But that would be pretty confusing 😄