
Clojure core.async Channels - siavosh
http://clojure.com/blog/2013/06/28/clojure-core-async-channels.html
======
swannodette
The ClojureScript support needs work but you can already express some
incredible things, those familiar with channels in Go should be dropping their
jaws that you can do this in the browser:

[http://gist.github.com/swannodette/5886048](http://gist.github.com/swannodette/5886048)

With timeout channels, the throttler is a beautiful 7 lines of clear code -
compare to the Underscore.js imperative mess:
[http://underscorejs.org/docs/underscore.html#section-64](http://underscorejs.org/docs/underscore.html#section-64)

I've been doing sophisticated JS UIs for 8 years now and had largely given up
on ever being able to really manage async complexity - neither JavaScript
Promises nor ES6 generators provide this level of process coordination and
clarity.

 _Very_ exciting times.

EDIT: Oh and don't forget you can pattern match on the result of a select
(alt!) with core.match to get Erlang style matching
[http://github.com/clojure/core.match](http://github.com/clojure/core.match)
:) All this from _libraries_

~~~
adambard
This has me super excited to build something cool with ClojureScript in the
near future. I'm used to using lamina
([https://github.com/ztellman/lamina](https://github.com/ztellman/lamina)) in
Clojure, but for working with the browser this is a whole new world.

~~~
jamii
I would be interested in seeing a comparison to lamina. I haven't had a chance
yet to think about it properly but my first impression is that core.async is
strictly more expressive but it couldn't implement something like lamina's
flow diagrams.

~~~
puredanger
Do you mean in constructing networks or visualizing them? I see no reason why
you couldn't do either with core.async.

~~~
prospero
Can you walk the chain of consumers and where they're forwarding the messages?
I didn't see anything like that in the code, but you're far more familiar with
it than I am.

~~~
puredanger
No, that's not there. I just mean _conceptually_ there is no reason we won't
have those kinds of debugging, visualization, etc tools eventually. This is a
work in progress. Zach has done a phenomenal job with that stuff in Lamina.

~~~
prospero
It seems to me that there are real obstacles to that; since the propagation of
messages from one channel to another requires a separate 'take' and 'put',
there's some halting problem-style obstacles to figuring out the causality of
how messages are propagated.

You can maybe create propagation operators that make these relationships
explicit, but that would require either eschewing the bare take/put methods,
or making sure the take/put behavior is in sync with whatever metadata you use
to describe it.

~~~
jamii
That's the kind of thing I was thinking about. The topology in lamina is
described by data whereas in core.async it's described by code. I'll have to
experiment more with both and see where the tradeoffs lie.

~~~
puredanger
It seems possible to me to build the lamina style topologies on top of
core.async.

~~~
snprbob86
In fact, that seems _preferable_ to me.

------
jwr
The last sentence is great: "I hope that these async channels will help you
build simpler and more robust programs." — I think it expresses the Clojure
philosophy very well.

All the previous Clojure concurrency constructs have made my programs
significantly simpler and more robust. I forgot what a deadlock is. And I
really like the practical approach that Clojure takes.

------
puredanger
In case it's useful, I've been working on this code-oriented companion
walkthrough for this post:
[https://github.com/clojure/core.async/blob/master/examples/w...](https://github.com/clojure/core.async/blob/master/examples/walkthrough.clj)

~~~
cgag
It is, thank you.

------
hueyp
For working with single value channels having go be an expression seems super
handy.

For multiple value channels my head keeps going to wanting to have map,
filter, etc with the channels and I'm thinking I'm missing something because
that would just be creating Rx / Observables.

~~~
snprbob86
You can build analogous versions of map/filter/etc on top of channels. The
core.async team is still focused on the primitives, but surely some higher
order operations will follow in the not too distant future. I suspect we'll
need to wait and see what patterns develop, since they will surely differ from
Go a little bit, due to the functional emphasis on Clojure.

I should also mention a key difference between channels and Rx/Observables:
The fundamental operations for observable sequences are subscribe &
unsubscribe. The fundamental operations for channels are put and take. In the
push sequence model, the consumer must give a pointer to itself to the
publisher, which couples the two processes and introduces resource management
burdon (ie IDisposable).

You can think of a pipeline from A to B in the following ways:

Sequences: B pulls from A

Observables: A pushes to B

Channels: Some process pulls from A and pushes to B

That extra process enables some critical decoupling!

~~~
hueyp
> since they will surely differ from Go a little bit, due to the functional
> emphasis on Clojure

Very interested in this. The trivial Go examples seem much more channel as
side effect.

While I still need to wrap my head around the differences (and thanks for the
explanation) one quick take away is how much easier it would be to write the
higher order operations with core.async channels versus observables.

~~~
snprbob86
I took a crack at this, if you're still interested:

[https://github.com/brandonbloom/asyncx](https://github.com/brandonbloom/asyncx)

------
mmorearty
I think this is extremely cool, but I'm struggling to understand the
disadvantage of actors compared to channels.

I think I understand the argument; just not sure I'm convinced by it. As I see
it, the argument goes like this: Suppose you want to set things up so that
block "A" is sending messages to block "B". With channels, block "A" just
sends a message to a channel that it was handed; it doesn't know or care what
is at the other end of the channel. The higher-level code that set things up
created a channel, and then passed that channel to both "A" and "B". So,
everything is loosely coupled, which is great.

With actors, on the other hand (so the argument goes), you would have to
create actors "A" and "B", and then send "A" a reference to "B", saying, "Hey
'A', here is the actor to which I want you to send your output." So now "A"
and "B" are wired up, but there is no explicit "channel" object connecting
them. I think the argument is that this is worse than channel, because there
is a tightly-coupled connection between "A" and "B".

But I don't think there is. In my scenario, some higher-level object still had
to create "A" and "B" and wire them up; so, they are loosely coupled.

I think it may, perhaps, be true that an actor system lends itself more
readily to tight coupling than a channel system does -- in other words, an
actor system might lead the casual programmer down the wrong path. Is that
what Rich meant when he said of actors, "Yes, one can emulate or implement
certain kinds of queues with actors (and, notably, people often do), but..." ?

If I'm missing something, I'd love to be enlightened!

------
pjmlp
And this shows how cool it is to have languages with minimal builtin
constructs and powerful abstractions for library writers.

------
gdubs
Interesting -- this looks similar to Lamina [1], which I've used an really
like.

1: [https://github.com/ztellman/lamina](https://github.com/ztellman/lamina)

------
z3phyr
People mention the JVM, which is the primary clojure platform. People say that
it is complemented by Clojurescript on the client side.

Inference : ClojureCLR seems to be dead :(

~~~
dmiladinov
_> Inference : ClojureCLR seems to be dead :(_

For at least a few years now.

I remember watching a Hickey talk where I think I recall him saying it was
even before 2011, but the exact name escapes me at the moment. However, I was
able to find this snippet from 2011:

 _Fogus: Clojure was once in parallel development on both the JVM and the CLR,
why did you eventually decide to focus in on the former?

Hickey: I got tired of doing everything twice, and wanted instead to do twice
as much._

[http://codequarterly.com/2011/rich-
hickey/](http://codequarterly.com/2011/rich-hickey/)

~~~
djpowell
No, that isn't right.

Years before the first release of Clojure, Rich used to maintain a parallel
build of Clojure for both platforms before concentrating on the JVM; but that
is ancient history, and isn't relevant to ClojureCLR today.

ClojureCLR is a port maintained by David Miller, Rich doesn't work on it, so
it isn't surprising that he hasn't made any comments about it.

Though it is true that the CLR implementation has much less adoption than the
JVM or JS implementations.

~~~
dmiladinov
Oh, I wasn't aware that ClojureCLR was still being maintained. All I knew was
the Hickey was no longer maintaining it.

Thanks for clarifying!

------
milos_cohagen
I spent a decent amount of time looking at Lamina, and while I liked the
concept, the implementation was, imho, a very complex set of macros and
dynamically built Java types and interfaces. It did not feel like the "clojure
way" and just following the code of a single message send was painful.

Will core.async be similar in it's implementation complexity?

~~~
_halgari
The channels themselves are a bit hard to understand because there's a careful
dance of locks/unlocks to get it all to work correctly, but it shouldn't be
hard to figure out.

The IOC go macro is huge (about 500/600 loc) but it's pretty straight forward.
Its really nothing more than a micro compiler that pareses clojure, does some
analysis on it, and spits it back out as clojure. If you've done any work with
compilers it should be very easy to understand.

------
gfodor
Can someone explain the benefits of channels vs event handlers for frontend
development in a non-abstract way like I'm five?

~~~
cgag
A more detailed explanation on how those IoC threads work would be awesome too
if someone's going to be explaining things to children would be great.

~~~
snprbob86
The IoC threads work by converting park-able functions into Single-Static
Assignment (SSA) form [1] and then compiled to a state machine. Essentially,
each time a function is "parked", it returns a value indicating where to
resume from. These little state machine functions are basically big switch
statements that you don't need to write by hand. This design is inspired by
C#'s async compilation strategy. See the EduAsync Series [2] on John Skeet's
blog, and the compilation post [3] in particular. Once you have these little
state machines, you just need some external code to turn the crank.

[1]:
[http://en.wikipedia.org/wiki/Static_single_assignment_form](http://en.wikipedia.org/wiki/Static_single_assignment_form)

[2]:
[http://msmvps.com/blogs/jon_skeet/archive/tags/Eduasync/defa...](http://msmvps.com/blogs/jon_skeet/archive/tags/Eduasync/default.aspx)

[3]:
[http://msmvps.com/blogs/jon_skeet/archive/2011/05/20/eduasyn...](http://msmvps.com/blogs/jon_skeet/archive/2011/05/20/eduasync-
part-7-generated-code-from-a-simple-async-method.aspx)

~~~
cgag
These are all excellent, that wiki page is surprisingly easy to understand.
Thanks.

------
pwpwp
What are the limitations of the GO macro, which I think makes these control
flow manipulations possible?

~~~
_halgari
There really aren't any semantic limitations of the go macro. About the only
thing that doesn't work, is that you can't use the "binding" macro inside of a
go macro (but using one outside the macro will work as expected). Go block
translation stops at function boundaries, so you can use a for loop inside a
go, but since the for returns a lazy seq, putting takes inside the body of a
for doesn't really make sense (or work), instead, use dotimes or a loop/recur.

Aside from that, go blocks do slow down the code they contain a bit. From my
tests, this slow down is within 1.5-2x the original code. However since go
block shouldn't be looping a ton, this shouldn't matter. Functions that are
called within go blocks are not modified, and so retain their original
performance semantics.

I hope that helps.

