(defmacro if-recv"Reads from port, binding to name. Evaluates the then block if theread was successful. Evaluates the else block if the port was closed."([[name port :as binding] then]`(if-recv ~binding ~then nil))([[name port] then else]`(let [~name (<! ~port)](if (nil? ~name)~else~then))))
I ran into the other half of this problem: If you expect nils to signify closed channels, then you can't leverage the logically false nature of nil without excluding explicit boolean false values. Given the pleasant syntax of if-let / <! pairs, I reworked my early experiments to use if-recv which is defined as follows:(defmacro if-recv"Reads from port, binding to name. Evaluates the then block if theread was successful. Evaluates the else block if the port was closed."([[name port :as binding] then]`(if-recv ~binding ~then nil))([[name port] then else]`(let [~name (<! ~port)](if (nil? ~name)~else~then))))I've considered some alternative core.async designs, such as an additional "done" sentinel value, or a pair of quote/unquote operators (see "reduced"), but nothing seems as simple as just avoiding booleans and nils, as annoying as that is. I'd be curious to here what Rich & team considered and how they're thinking about it. However, my expectation is that the nil approach won't change, since it's pretty much good enough.
> have every other value come wrapped in a Just or Some-like constructor
That's what I meant by "a pair of quote/unquote operators"
`(let [~name (<! ~port)]
(if (end-of-stream? ~name)~else~then))))
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/pF9FEP7b77U/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
On 27 August 2013 20:45, Timothy Baldridge <tbald...@gmail.com> wrote:
The reason for not allowing nils isn't a complex one, and basically boils down to the following:
a) to avoid race conditions, we need a single value to signal "the channel is closed". As mentioned, nil is the obvious choice for this as it matches lazy seqs and fits well with the rest of clojure:
Agreed that you want a single sentinel value.It doesn't match lazy-seqs at all though: lazy seqs can contain nils just fine. There's a big difference between (next some-lazy-seq) [which could be nil, indicating an empty sequence] and the actual values in the seq [which could also be nil but don't indicate the end of the seq].
(when-let [v (<! c)]
(process v))
If we chose a different value, this becomes much more ugly:
(let [v (<! c)]
(when-not (= v :async/closed)
(process v)))
This can be solved easily by providing a macro or some other predicate that knows how to check for the sentinel value correctly. e.g.(when-more [v (<! c)](process v))b) I question if there are any valid uses for putting nil in a channel. With all due respect to all who have written here, thus far, every complaint about nils and channels boils down to a conversion from seqs to channels. This is the wrong way to look at the problem. Channels are co-ordination primitives not data structures. Simply because a lazy seq looks like a channel, doesn't mean that they should be treated as such.
In all the core.async code I've written I've never had to put a nil in a channel, so I'm left with the uncomfortable conclusion that most complaints on this subject are contrived. I could be wrong, but I just haven't seen a valid use case yet.
To me it's all about consistency with other Clojure constructs. You can safely put nils in sequences, vectors, lists, sets etc.. nil is a valid "value" just like anything else. So why can't you put them in a channel?Two use cases I have encountered that motivate this:a) what if you want to send a sequence through a channel? Since nil as a value represents the empty sequence, you have to put in some extra special case handling with the current core.async model.b) what if you want to write generic code to send all the values in an arbitrary collection through a channel? you would have to wrap/unwrap nils at either end to make this work currently.
Both of these, I think, are reasonable and common enough use cases that it's worth supporting them elegantly rather than forcing users to implement their own nil-wrapping functionality.
This all being said, there really isn't a technical reason to not allow nils, it just simplifies much of the design and that probably translates to better performance. So the restriction could be lifted if a rock solid reason could be found, but as of yet, I haven't seen it.
I don't believe there is any noticeable performance difference between checking for nil and checking if a value is identical? to some sentinel value (which would presumably be static, final, immutable and hence very well optimised by the JVM). In addition, not allowing nils just means you have to do extra work to wrap/unwrap nils as a user - which is almost certainly a net loss on overall performance.Still, I think consistency is more significant than the performance argument in this case.
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/pF9FEP7b77U/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
To me it's all about consistency with other Clojure constructs. You can safely put nils in sequences, vectors, lists, sets etc.. nil is a valid "value" just like anything else. So why can't you put them in a channel?
a) what if you want to send a sequence through a channel? Since nil as a value represents the empty sequence, you have to put in some extra special case handling with the current core.async model.
Both of these, I think, are reasonable and common enough use cases that it's worth supporting them elegantly rather than forcing users to implement their own nil-wrapping functionality.
The reason for not allowing nils isn't a complex one, and basically boils down to the following:
a) to avoid race conditions, we need a single value to signal "the channel is closed". As mentioned, nil is the obvious choice for this as it matches lazy seqs and fits well with the rest of clojure:
(when-let [v (<! c)]
(process v))
If we chose a different value, this becomes much more ugly:
(let [v (<! c)]
(when-not (= v :async/closed)
(process v)))
b) I question if there are any valid uses for putting nil in a channel. With all due respect to all who have written here, thus far, every complaint about nils and channels boils down to a conversion from seqs to channels. This is the wrong way to look at the problem. Channels are co-ordination primitives not data structures. Simply because a lazy seq looks like a channel, doesn't mean that they should be treated as such.
In all the core.async code I've written I've never had to put a nil in a channel, so I'm left with the uncomfortable conclusion that most complaints on this subject are contrived. I could be wrong, but I just haven't seen a valid use case yet.
This all being said, there really isn't a technical reason to not allow nils, it just simplifies much of the design and that probably translates to better performance. So the restriction could be lifted if a rock solid reason could be found, but as of yet, I haven't seen it.
Timothy Baldridge
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/pF9FEP7b77U/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
On 27 August 2013 20:45, Timothy Baldridge <tbald...@gmail.com> wrote:
The reason for not allowing nils isn't a complex one, and basically boils down to the following:
a) to avoid race conditions, we need a single value to signal "the channel is closed". As mentioned, nil is the obvious choice for this as it matches lazy seqs and fits well with the rest of clojure:
Agreed that you want a single sentinel value.It doesn't match lazy-seqs at all though: lazy seqs can contain nils just fine. There's a big difference between (next some-lazy-seq) [which could be nil, indicating an empty sequence] and the actual values in the seq [which could also be nil but don't indicate the end of the seq].
First, I'd hope that sentinel values would be handled by the back-endimplementation, as we're seeing core.sync implemented on othersystems like ZeroMQ already.Second, as (Object.) doesn't play nicely over the wire, a random UUIDor similar value would be much preferred.
Third, I'd recommend reviewing,to understand why core.async is not just a better queue.Fourth, if you dislike how core.async works, just wrap it in your ownlibrary so it works the way you'd like.It looks like "core.async-with-nil" is available on Clojars. ;)