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:
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 :) All this from libraries
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) in Clojure, but for working with the browser this is a whole new world.
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.
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.
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!
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!
It's not dead yet. David Miller has been untiringly maintaing Clojure's CLR implementation for years now. Clojure CLR right now has feature parity with Clojure JVM. core.async is still alpha (it's also an external lib), I bet that once it reaches some stability David will add support for CLR in a weekend (as it has been usually).
Unfortunately that doesn't mean there is much community interest in the CLR port though, but David's relentless stewardship of the project is relentless.
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.
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?
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.
It's not quite clear what patterns will emerge with respect to UI event handlers, so there really isn't an ELI5 answer yet in the View layer. The backend advantages are well covered by the Go language community, so check out Rob Pike's talk [1]. As for other parts of the frontend, like the model layer, channels can lead to a major simplification for the callback hell involving multiple coordinated network requests or for transforming and processing events off a message socket, like that of Socket.io.
Channels allow you to write code that "pulls" events off of a stream or enumerate-like structure. This can dramatically simplify event-heavy front end code.
Bacon.js is a library that exposes a similar technique (FRP), and their readme outlines the benefits in tangible terms
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.
[+] [-] swannodette|12 years ago|reply
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
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 :) All this from libraries
[+] [-] adambard|12 years ago|reply
[+] [-] swannodette|12 years ago|reply
[+] [-] bkirkbri|12 years ago|reply
I don't see this as much of a downside though, considering the issues one can have with selective receives.
[+] [-] jwr|12 years ago|reply
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.
[+] [-] unknown|12 years ago|reply
[deleted]
[+] [-] puredanger|12 years ago|reply
[+] [-] cgag|12 years ago|reply
[+] [-] hueyp|12 years ago|reply
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|12 years ago|reply
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!
[+] [-] mmorearty|12 years ago|reply
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|12 years ago|reply
[+] [-] gdubs|12 years ago|reply
1: https://github.com/ztellman/lamina
[+] [-] z3phyr|12 years ago|reply
Inference : ClojureCLR seems to be dead :(
[+] [-] zaph0d|12 years ago|reply
Example: https://groups.google.com/forum/#!topic/clojure/Kvefcae2W6s
Project Source: https://github.com/clojure/clojure-clr
[+] [-] dmiladinov|12 years ago|reply
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/
[+] [-] milos_cohagen|12 years ago|reply
Will core.async be similar in it's implementation complexity?
[+] [-] _halgari|12 years ago|reply
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.
[+] [-] kencausey|12 years ago|reply
[0] https://github.com/clojure/core.async/
[+] [-] gfodor|12 years ago|reply
[+] [-] snprbob86|12 years ago|reply
[1]: http://talks.golang.org/2012/concurrency.slide#1
[+] [-] nwjsmith|12 years ago|reply
Bacon.js is a library that exposes a similar technique (FRP), and their readme outlines the benefits in tangible terms
https://github.com/raimohanska/bacon.js
[+] [-] cgag|12 years ago|reply
[+] [-] pwpwp|12 years ago|reply
[+] [-] _halgari|12 years ago|reply
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.