[ANN] core.async-0.2.371

1,129 views
Skip to first unread message

Alex Miller

unread,
Oct 28, 2015, 5:06:41 PM10/28/15
to Clojure
I am happy to announce a long-overdue core.async release.

Dependency info:  [org.clojure/core.async "0.2.371"]

There are a few new features in this release:

1) promise-chan is a function that returns a new kind of channel (with a custom buffer) with promise semantics. Specifically, channels make a one-time transition to having a deliverable value. promise-chan takes an optional transducer, and an optional exception-handler (like chan). A promise channel can take exactly one value that consumers will receive. Once full, puts complete but val is dropped (no transfer).
Consumers will block until either a value is placed in the channel or the channel is closed (and nil will be delivered). 

2) offer! and poll! are two new non-blocking functions available on channels.

offer! puts a val into a channel if it can do so immediately and will never block. Returns true if offer succeeds.
poll! takes a val from a channel if it can do so immediately and will never block. Return a value if successful, nil otherwise.

All changes:
  • ASYNC-103 - NEW promise-chan
  • ASYNC-104 - NEW non-blocking offer!, poll!
  • ASYNC-124 - dispatch multiple pending takers resulting from expanding transducer
  • ASYNC-101 - async/reduce now respects reduced
  • ASYNC-112 - replace "transformer" with "transducer" in deprecation messages
  • ASYNC-6 - alts! docs updated to explicitly state ports is a vector
  • Support (try (catch :default)) in CLJS exception handling
  • Use cljs.test
  • Updated tools.analyzer.jvm version (and other upstream deps) - fixes various analyzer errors

Daniel Compton

unread,
Oct 28, 2015, 5:17:57 PM10/28/15
to Clojure
Thanks Alex! I appreciate the work you put in to get this 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.
For more options, visit https://groups.google.com/d/optout.
--
Daniel

Robin Heggelund Hansen

unread,
Oct 28, 2015, 6:52:58 PM10/28/15
to Clojure
Great work Miller & Team :D

Bobby Calderwood

unread,
Oct 28, 2015, 10:23:07 PM10/28/15
to Clojure
Bravo, great work Alex and team!  Also appreciate the new versioning scheme.

Rangel Spasov

unread,
Oct 28, 2015, 10:34:11 PM10/28/15
to Clojure
That's great, thanks everyone involved in this release. Already made use of offer! : ) .

Peter Taoussanis

unread,
Oct 29, 2015, 12:12:44 AM10/29/15
to Clojure
Excellent \o/, much appreciated!

Martin Raison

unread,
Oct 29, 2015, 1:26:13 AM10/29/15
to Clojure
Awesome, thanks!

Derek Troy-West

unread,
Oct 29, 2015, 6:05:04 AM10/29/15
to Clojure
Hi Alex, thanks for your efforts and the update.

I have a few questions about promise-chan if you have a moment.

Firstly, why provide promise-chan rather than requiring (chan (buffers/promise)). The buffer provides the promise semantics and we don't similarly have sliding-chan, dropping-chan, etc. Is this an effort to delineate channels with promise-buffers from the vanilla variety?

Secondly, I've perhaps mistakenly considered channels more as a conduit between processes rather than a replacement for single assignment message passing. I get the value of using a channel and the core.async state machine for achieving promise-like semantics in a non-blocking way, and I can achieve that for a single consumer with a normal channel, the rub is this new buffer allows repeated consumption of a single value, and my previous understanding of channel semantics 'a channel will return nil when it is closed after some number of takes' no longer holds in all cases, because a promise-buffer is never exhausted.

Practically I'm sure I'll always be aware of the buffer-flavour of a channel that I'm using, but now we have a variety of channels for which many channel operations do not make sense, i.e.:

(async/go
  (prn "result" (async/<! (async/into [] some-chan)))

Am I right in suggesting a promise-channel is not really a channel, it's a promise masquerading as a channel because of the value of having the state machine as the mechanism of resolving assignment. This is clearly a welcome addition, perhaps my understanding of channel semantics needs an upgrade.

Alex Miller

unread,
Oct 29, 2015, 9:49:55 AM10/29/15
to Clojure


On Thursday, October 29, 2015 at 5:05:04 AM UTC-5, Derek Troy-West wrote:
Hi Alex, thanks for your efforts and the update.

I have a few questions about promise-chan if you have a moment.

Firstly, why provide promise-chan rather than requiring (chan (buffers/promise)). The buffer provides the promise semantics and we don't similarly have sliding-chan, dropping-chan, etc. Is this an effort to delineate channels with promise-buffers from the vanilla variety?

You should consider the promise buffer to be an implementation detail. There are multiple ways to implement a promise channel and in the future it's possible the implementation might change to using a custom channel implementation instead. The feature we are providing here is the promise channel and that's reflected in the api.
 
Secondly, I've perhaps mistakenly considered channels more as a conduit between processes rather than a replacement for single assignment message passing. I get the value of using a channel and the core.async state machine for achieving promise-like semantics in a non-blocking way, and I can achieve that for a single consumer with a normal channel, the rub is this new buffer allows repeated consumption of a single value, and my previous understanding of channel semantics 'a channel will return nil when it is closed after some number of takes' no longer holds in all cases, because a promise-buffer is never exhausted.

Nothing about channels guarantees they will be closed after some number of takes, so I disagree with this definition.
 
Practically I'm sure I'll always be aware of the buffer-flavour of a channel that I'm using, but now we have a variety of channels for which many channel operations do not make sense, i.e.:

(async/go
  (prn "result" (async/<! (async/into [] some-chan)))

Am I right in suggesting a promise-channel is not really a channel, it's a promise masquerading as a channel because of the value of having the state machine as the mechanism of resolving assignment. This is clearly a welcome addition, perhaps my understanding of channel semantics needs an upgrade.

The promise channel fulfills the channel api, so as far as I'm concerned, it is a channel (but one with special properties).

I should have mentioned that promise-chan, offer!, and poll! were discussed by Rich at last year's Conj talk if anyone is interested in hearing him talk about these: https://youtu.be/4KqUvG8HPYo?t=52m6s

Alex

Marian Schubert

unread,
Oct 29, 2015, 11:52:08 AM10/29/15
to Clojure
Awesome!

I did try it on our project and it failed at one location with:
CompilerException java.lang.UnsupportedOperationException: Can only recur from tail position

Minimal example which still fails (alts! + case + recur combination) is:

(defn this-fails-to-compile [x]
  (go
    (loop []
      (let [_ (async/alts! [,,,])]
        (case :whatever
          :something
          (recur))))))

I guess this should work(?) Should I report it somewhere?

Thanks,
Marian

Alex Miller

unread,
Oct 29, 2015, 2:15:35 PM10/29/15
to Clojure
I'll take a look, thanks.

Derek Troy-West

unread,
Oct 29, 2015, 5:01:07 PM10/29/15
to Clojure
 
Secondly, I've perhaps mistakenly considered channels more as a conduit between processes rather than a replacement for single assignment message passing. I get the value of using a channel and the core.async state machine for achieving promise-like semantics in a non-blocking way, and I can achieve that for a single consumer with a normal channel, the rub is this new buffer allows repeated consumption of a single value, and my previous understanding of channel semantics 'a channel will return nil when it is closed after some number of takes' no longer holds in all cases, because a promise-buffer is never exhausted.

Nothing about channels guarantees they will be closed after some number of takes, so I disagree with this definition.

To be clear, I didn't mean that a channel is guaranteed to be closed, rather that previously I could count on the ability to drain a channel after I have chosen to close it. Closing a promise-chan which has received a value has no impact on later takes, it is indistinguishable from a channel with an infinite buffer filled with a single value, which feels like a deviation from the thought that unbounded buffers are bad. The example in my previous message will OOM when used with a promise-chan.

This new behaviour doesn't directly contradict the close! dosctring - "Data in the channel remains available for taking, until exhausted, after which takes will return nil", but that description could leave you with the expectation that data is exhausted.

I guess I'm trying to understand if this new channel type is intended to be used with the battery of existing channel macros (pipe, reduce, pipeline, mult, etc.), where it may act as an unbounded, uncloseable producer of values, or if it's really only intended to be used as a promise which can be resolved in a non-blocking manner using put, take, >!, <!, !!>, !!<.

If it's the latter, would it make sense to rename promise-chan to promise, i.e. a core.async promise type who's implementation happens to be a channel, and expand only those macros relating to puts and takes to accept chan-or-promise.

Thanks again.

Lucas Bradstreet

unread,
Oct 29, 2015, 5:07:00 PM10/29/15
to clo...@googlegroups.com
Fantastic release. I've been waiting for poll! and offer! for a while.

What is the recommended way to check whether a channel is closed?
Previously if the channel returned nil, then we know the channel is
closed, however with poll! we can't tell between the channel being
closed or just empty.

Thanks again,

Lucas

Alex Miller

unread,
Oct 29, 2015, 5:10:43 PM10/29/15
to Clojure
To be clear, I didn't mean that a channel is guaranteed to be closed, rather that previously I could count on the ability to drain a channel after I have chosen to close it. Closing a promise-chan which has received a value has no impact on later takes, it is indistinguishable from a channel with an infinite buffer filled with a single value, which feels like a deviation from the thought that unbounded buffers are bad. The example in my previous message will OOM when used with a promise-chan.

The buffer is not unbounded - it is of size 1.
 
This new behaviour doesn't directly contradict the close! dosctring - "Data in the channel remains available for taking, until exhausted, after which takes will return nil", but that description could leave you with the expectation that data is exhausted.

I guess I'm trying to understand if this new channel type is intended to be used with the battery of existing channel macros (pipe, reduce, pipeline, mult, etc.), where it may act as an unbounded, uncloseable producer of values, or if it's really only intended to be used as a promise which can be resolved in a non-blocking manner using put, take, >!, <!, !!>, !!<.

It is primarily useful as a promise, but it can be useful to combine it with other channel ops with alts etc. But there may be other use cases where it's useful.
 
If it's the latter, would it make sense to rename promise-chan to promise, i.e. a core.async promise type who's implementation happens to be a channel, and expand only those macros relating to puts and takes to accept chan-or-promise.

We are not going to change the name - it is a channel with promise semantics.

Gregg Reynolds

unread,
Oct 29, 2015, 6:01:17 PM10/29/15
to clo...@googlegroups.com


On Oct 29, 2015 4:10 PM, "Alex Miller" <al...@puredanger.com>

>
> The buffer is not unbounded - it is of size 1.

Is it correct or reasonable to think of it as a constant channel?

Rangel Spasov

unread,
Oct 29, 2015, 6:35:17 PM10/29/15
to Clojure
I assume this is a pretty reliable way:

(:require [clojure.core.async.impl.protocols :refer [closed?]])

(closed? a-chan)

Derek Troy-West

unread,
Oct 29, 2015, 7:18:47 PM10/29/15
to clo...@googlegroups.com
Thanks Alex, I think I can sum up my confusion as: I thought channels were single take, but understand now that is not necessarily the case.

In his 2013 Strange Loop talk Rich described first-class channels and the subset of state that they expose:

"But I think what's really cool about first class channels is that they expose a subset of state, and it's a very interesting and far safer one, which I'll call flow state, right, which is just about all you can do with this kind of state is put stuff in it, and then you really don't know anything more about it. Or you can take stuff out of it, and you really don't know how it got there."

In particular he drew an analogy that really resonated with me, shared-state=coat-hook, flow-state=conveyor-belt.

"It's going to go on a conveyor belt. It's going to, you know, somewhere else. It's going to go down that chute and you can't recover it. The UPS truck is going to drive away. And that's good because you can't possibly have your life depend on going back and seeing the same thing there again. So it's not really like state the way shared state and place oriented state is. Flow state is much simpler. It's much easier to reason about and much safer because you do not have this expectation of coming back"

Understanding the limitations of analogies, does this new channel adhere to that concept of flow-state? Is it a conveyor-belt or a coat-hook? Apologies if the concept of what state a channel exposes has been updated since then and I've missed the point. I found that particular analogy a powerful one when trying to understand channels so perhaps that's why I'm hung up on it.


--
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/NdQMUs1mTUU/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

Alex Miller

unread,
Oct 29, 2015, 7:33:18 PM10/29/15
to Clojure

On Thursday, October 29, 2015 at 5:35:17 PM UTC-5, Rangel Spasov wrote:
I assume this is a pretty reliable way:

(:require [clojure.core.async.impl.protocols :refer [closed?]])

(closed? a-chan)

Being something under "impl", you should treat this as an implementation detail subject to change. However, we have been discussing the possibility of a public version of this.

Alex Miller

unread,
Oct 29, 2015, 7:41:02 PM10/29/15
to Clojure


On Thursday, October 29, 2015 at 6:18:47 PM UTC-5, Derek Troy-West wrote:

Understanding the limitations of analogies, does this new channel adhere to that concept of flow-state? Is it a conveyor-belt or a coat-hook?

I don't think it's exactly either of those (hey, analogies). A promise-chan is like a xerox machine. When the thing to be copied arrives (or the machine is closed without anything to copy), it copies forever.
Reply all
Reply to author
Forward
Message has been deleted
0 new messages