This looks nice. Less readable than Go (is there GoScript yet?), but still fairly straightforward.
One remark I'd like to make is that I don't see this as having a big advantage over FRP.
The examples with mouse coordinates look like an imperative approach to FRP. With the latter, one wouldn't have to explicitly set up a channel and read from it -- that is an implementation detail in FRP.
To emphasize the contrast, I'd call this channel-based approach an Imperative Communicating Programming (ICP). Your code does not react to changes like it does with FRP. Instead, you as a programmer ask for changes, very often in an infinite loop, and then transform those changes into a side-effect in the browser.
On one hand, channels + goroutines provide a single abstraction with which it is kind of straightforward to implement various concurrently executing threads of code. On the other hand, those infinite loops and their proliferation in the channel-based code will definitely cause some people to cringe and go with FRP instead.
I don't think FRP is as applicable as channels to as many use cases. Channels are more low level and therefore more potent. But for what FRP does, it provides a better (read "more concise", or "declarative") abstraction for it.
Note that in some ways core.async is a bit more flexible than Go - for example you can dynamically select over a changing list of channels. I've found this to be really handy especially when you're receiving channels on channels.
you can do everything in an FRP style if you like with core.async, it's flexible enough for that. But I think for less toy examples this is undesirable as you allude.
Go has a trick where you can set a channel to nil in order to get its functionality ignored in your select. If you gave the select all the possible channels and turned them on and off rather than adding/removing channels you can obtain similar functionality.
I've written a few client side projects in ClojureScript, and I haven't been totally happy with the way I end up doing event handling. I'm excited to give this a try.
By the way, I love that this is the sort of thing you can do with just a _library_ in Clojure.
It's pretty brand new, so I don't think there are any "real" projects using it yet. But you can check out this example repo from David Nolen (the blog posts author) for some more examples.
Brilliant! I had always hoped CoffeeScript or somehow a JS library would add blocking channels like this. Writing JavaScript is a pain after being spoilt by Go.
I'm quite curious about what the compiled code looks like. (On a mobile now, I'll have a look when I can.)
Compiling ClojureScript to JavaScript doesn't yield anything too surprising, and that's great.
But core.async does some amazing stuff to actually analyze the code inside a "go" block and recompile it as a state machine so that a single thread can be "parked" and resumed. This allows core.async to work without multiple threads. It does this all at macro expansion time. This is the sort of thing that really demonstrates the power of Lisp. There's no way to do this without modifying the compiler in a language without a macro system.
So the output of a core.async program is going to be startlingly different than a normal ClojureScript program, because of the way it takes your "go" blocks apart and rewrites them when the macro is expanded.
There is no runtime and note that's 22k gzipped. This is far smaller than jQuery + Underscore.js + persistent data structure library + stream implementation + async control flow library gzipped which is what you're getting. jQuery alone is 33k gzipped.
One thing that is not covered in the post is how should we clean up those goroutines that are blocked on channels. I'm not familiar with internal workings of core.async, so I'm looking at it from Go's point of view.
Imagine that in the search example, inside the `google` function, we want to read from 100 channels. And each call to `fastest` is supplied with 100 arguments. If on average we get 100 results back in the allotted time, 900 goroutines will be left in a blocked state waiting for someone to read from their respective channel.
I'm guessing those goroutines are cheap and are implemented as some sort of state machine that maps to callbacks. But all those blocked goroutines should still incur runtime cost. Will the GC kick in collect those?
If this issue is really there (i.e. if I'm not mistaken), imagine what will become of your one-page-web-app after you click on that Search button a dozen of times.
Thanks for the info! I'm willing to get my hands dirty and see how it's all implemented there.
Just for the record, in Go, if you leave a goroutine blocked on a channel, the channel and the goroutine will remain on the heap unless you read everything from the channel (normal way) or close it (and get a runtime panic).
Can some of the clojure and clojurescript gurus take some time to write beginner friendly, hand holding tutorials? I mean, I can see the power of these technologies but the very thought of climbing a learning curve AS WELL AS a getting started curve ( that too without any external help! ) is the single most important thing that turns people off.. especially me. My general complaint is with regard to how many tutorials and even books are out there on these topics! Countable, with the fingers in one hand perhaps!
If you're referring to core.async specifically, keep in mind it's very new. Its readme[1] goes as far as to say, "This library is work-in-progress, experimental, broken etc. It should only be used by people working on it, ATM." With more maturity will surely come more examples and tutorials.
But if you mean Clojure(Script) in general, I kind of agree. There are some beginner friendly tutorials; lately Andrew Brehaut's intro to the Clojure web stack[2] has been smooth going for me, despite being a bit dated. But you have to look around. In particular, I've found the small-scale mechanics of the language easy to pick up via Clojure Koans[3], 4clojure[4], and ClojureDocs[5]. What's missing the most IMHO is approachable introductions to the big-picture concepts. Especially the less generically lispy, more Clojure-specific parts, concerned with concurrency and state. (I hear the book Clojure Programming by Emerick et al. is good for that... have yet to read.)
I kind of understand Clojure not being easy or quick to learn, because powerful tools take time to master. But even with that said, it could be better. When I get confident with Clojure I hope to be part of a new wave of Clojurists who write really beginner-friendly tutorials :) I think it won't be the "gurus" who write them, but regular joes/janes like you and me for whom the initial difficulty of this stuff is still a fresh memory.
I looked at Clojure at one point and fell in love with the lispy but not too parenthetical syntax. I left it behind after a while because I couldn't deal with the awful exception tracebacks which were supremely unhelpful when debugging. Most of the more arcane messages had to do with fundamental syntax errors, which was probably not as big a deal for an experienced programmer. However, it made it very difficult for a bigger to debug when you couldn't so much as see the line number on which you made the mistake.
They may have fixed that recently, and this CSP implementation is really cool, so I may take a look back at the language.
I agree that the exception traces are pretty awful. Having used Clojure for a couple of years now, all I can say is that you do get used to them. Despite the complexity of the libraries and the 'foreign-ness' of the concepts coming from an imperative background, Clojure is fundamentally a simple language/environment.
I've developed a sense for the types of mistakes that I usually make and I tend to scan the stacktrace for my own namespace, ignoring the clojure.* lines[1]. Once I see what line caused the problem, if it's not a typo then it just takes less than a minute to reason about what's going on and find a solution. The REPL helps a lot, because you can make changes on the spot or try out a modified code fragment.
As some others have said here: yes, it is difficult to make the adjustment from an imperative way of thinking but it's really, really worth it once you get there.
[1] You can probably even de-emphasise clojure.lang stacktrace lines in Eclipse using the Grep Console plugin but I'm not sure about this off the top of my head, because the Eclipse console might override stderr output.
Oh, I'm familiar with functional programming. I had played around with Scheme for a bit before moving to Clojure and used Common Lisp and OCaml afterwards. It was always just the terrible stack traces and really long startup times that drove me away.
"languages like CoffeeScript, Dart, and TypeScript. These languages fail to address the single largest source of incidental complexity for any sizeable client side application - concurrency."
I didn't realize Dart supports such an actor-like primitive. Neat.
That said, I agree with Rich's analysis in the "Wither actors?" section of the core.async announcement:
"They still couple the producer with the consumer. Yes, one can emulate or implement certain kinds of queues with actors (and, notably, people often do), but since any actor mechanism already incorporates a queue, it seems evident that queues are more primitive."
I'm not sure you actually read the post, isolates do not address the same issues at all - you can't share data between isolates and you can't put isolates onto isolates limiting coordination possibilities - all of these are possible with channels.
I think you're taking my use of the word out of context of the whole post. I'm talking about the concurrency that is imposed by the very nature of client side programming - you have no choice. Isolates don't allow to you coordinate the concurrency that is already present.
dart:async also provides the Stream abstraction that can be compared to Clojure(Script)'s core.async channels.
Libraries like dart:html and dart:io express their asynchronous APIs in terms provided by dart:async so the model is unified across the stack (e.g. HTML events are delivered through streams out of the box).
Here is the first example and third examples from David's post rewritten to Dart
Excellent! Though this is precisely illustrative of what I was talking about with respect to JavaScript likely needing promises and generators for non-trivial work. Is Dart going to add sugar over both futures and streams?
I'm on the Dart team and spent time talking to the corelib team when Streams were first added. I had a proposal at the time to unify streams and futures. My contention was that a future was just a stream that only emits one value and then closes.
Florian convinced me that that was trying to hard to jam them together. Instead, there's methods to convert from one to the other when you want:
new Stream.fromFuture()
Future<List> Stream.toList()
Future.asStream()
Asynchrony is still quite hard to work with compared to a language with coroutines and blocking, but the Future/Stream distinction doesn't seem to cause many problems in practice.
Yes but I'd argue that your original intuition was probably correct - we don't have this distinction in core.async because we have channels and they can be used for both one offs and stream processing. Of course the reason to eliminate the distinction would be to simplify sugar over asynchronous code a la F#, C#, Scala and now core.async. mraleph seemed to suggest this is being considered for Dart.
Well, this dualism is used to communicate important distinction: single value vs. multiple values, so streams return futures in the places where it is absolutely meaningful to do so e.g. first element of a stream can be obviously viewed as a one element stream but it is also a Future because it is at most one value.
Does anyone have any idea why the mouse-over examples are a more CPU intensive task for Firefox? On my laptop, moving the mouse a lot results in 15% CPU usage, and if I jitter it fast enough it maxes out at 22%. That won't lock up the browser if I'm not doing anything else but still feels like it will drain the laptop battery notably faster with intensive use, if you catch my drift.
With Chrome I can't get it above 4%. IE 10 is somewhere in the middle with 10% (and also has a bug when resizing the window, giving Y values like 2236.65990234375, with the fraction part being constant).
EDIT: Both Opera 12 and Opera 15: 6 to 8%
Also, the second/third examples seem a bit laggy with all browsers: if I shake the mouse quickly the numbers keep updating for a while after I stop. I guess that this is because instead of showing the most recent value it shows all the location values, but the screen updates are slower than the mouse updates?
Either way, when the mouse stops moving while the numbers still update, Firefox only takes 6% of the CPU. Not sure if that clarifies anything, but it might help identify the cause.
I don't think you can come to any conclusions from these numbers. I see the exact same CPU utilization across browsers if I replicate the styles and write this JavaScript instead:
var loc = document.getElementById("location");
window.addEventListener("mousemove", function(e) {
loc.innerHTML = e.pageX + ", " + e.pageY;
});
Which is saying something, the ClojureScript is doing a lot more work and it ain't slow ;)
I suspect all the cost is from repainting large areas at the speed of mouse movement. But the mouse examples are for illustration - in production code you'll probably want to throttle (which would avoid piling up the repaints you observed on slower machines) as well as address the IE 10 edge case.
Well, my question was why one browser was slower than the other - I didn't expect ClojureScript to be the cause of the problem. I was just curious about what might cause the difference.
> I suspect all the cost is from repainting large areas at the speed of mouse movement.
I'm not really a web dev, but isn't this a reflow, not a repaint? Could that be the reason for the difference in performance too? What happens if you use canvas instead of changing a div?
BTW, I forgot to mention this in the previous comment, but: this is pretty cool! I echo the "being spoilt by Go" sentiments of others, so I'm happy to see this library.
Maybe it is just me, but I prefer logically to think of actors (or processes) as concurrency primitives not channels, kind of like what Erlang has. For example in go the primary question is "what channel to I write or read from?", in Erlang it is "what is the pid of the process, so I can send it a message?".
Channels are a neat generalization of what Erlang has. Btw the first example used in the post isn't all that "fall out of your chair". Here is the same implemented in pure JS using my toy IO.js library - https://gist.github.com/srikumarks/5992336
I like to view pids as a special case of a channel. A pid is just the one-and-only channel of a process, and a process cannot create more. If I'm not mistaken, Erlang advocates creating processes for very specific tasks, so most of the time one channel is enough. But if you ever find yourself writing processes that check the "type" of an incoming message and dispatch that to different handlers, then consider if it could be easier if each message type came in on a different channel.
The security implications of channels are neat too, you can treat them as capabilities to specific parts of a process' API.
Channels by themselves are not a concurrency primitive. If you try to write and read from a channel in the same context, you'll deadlock. Goroutines (or "go blocks") provide the concurrent semantics, channels are a means of communication.
In Go you don't actually ask "what channel do I read from?". You are handed a channel (as a function argument or as a value from another channel) which you can then use to communicate. From the encapsulation point of view, pids and channels are very similar -- the code using the channel/pid doesn't have to know anything about the other end.
But the semantics of channels and message passing are very different: always synchronous vs asynchronous by default.
The last time I tried it I didn't find a broadly accepted server-side framework that automatically compiles ClojureScript assets, but I haven't looked in a while.
Why wouldn't you just deploy the compiled js as a static asset? If you want to autocompile it while developing, leiningen + cljs-autobuild handle that well.
Any time you're dealing with multiple control paths (regardless of if they are real threads, logical threads, callbacks, asynchronous messaging, etc), small changes in timing or synchronization will introduce non-determinism. For example, you don't know how quickly a remote service is going to respond.
If you had some code that communicated with channels and used only a single real thread without using any non-deterministic operations, then you'd actually have deterministic choice. So if you write a unit test with some mock services, you would always read from a particular service because the execution is truly deterministic. That might also mean that if two services respond at similar times (even non-deterministically) and you prefer one over another in at a choice, you could wind up unfairly ignoring one of the channels.
To combat this issue, Golang & core.async introduce randomness when multiplexing. If two channels are both ready, one is selected at random.
You won't know which channel is going to give you a value ahead of time, it depends on runtime conditions.
But really, you don't care if the user pushes the keyboard or moves the mouse first, you just want to handle each event as it happens in essentially a sequential manner.
The default for multiplexing is non-deterministic choice. You can specify the :priority flag [1] if you want deterministic choice when multiplexing. Reads and writes will block for a non-deterministic amount of time, but their sequencing will be deterministic. That's the beauty of the CSP model: You can achieve some level of local determinism within a particular thread of control. Makes it much easier to reason about the non-deterministic outside world.
the article is a bit of a cheap shot against Typescript. They have had async in the roadmap the last 6 months at least (and the public project is only about 10 months old).
IIRC, async is the next big feature on the roadmap, and going to be in before v1.0 (v0.9, released 2 months ago, added generics)
given the pace of development, typescript v1 should be done in 6 months or less.
I don't see how that makes it a cheap shot. Everyone is drawing from the same pool of ideas that were discovered decades ago. Could you be more specific?
typescript is very popular for how young it is. so given that the thing is still a very young, alpha product, saying that they are neglecting feature X is a bit too much. better to just not even mention it.
Yeah, the rhetoric at the beginning is a bunch of cheap shots at different languages. Having said that, it is clearly separated from the rest of the post by the line: Enough rhetoric, let's see how core.async works in practice.
As for language features? core.async is not a feature of Clojure/ClojureScript; it's merely a library.
IcedCoffeeScript is neat but as far I can tell it lacks anything equivalent to non-deterministic choice (select in Go, alts! in core.async) eliminating many useful patterns especially for UI programming.
Yeah I've seen this before though it doesn't really allow you to share arbitrary data as it relies on Web Workers (if you share data with a worker it will disappear from the sending process). I'd also be concerned about the performance of the implementation, core.async adopts C#'s approach which is pretty light weight.
One remark I'd like to make is that I don't see this as having a big advantage over FRP.
The examples with mouse coordinates look like an imperative approach to FRP. With the latter, one wouldn't have to explicitly set up a channel and read from it -- that is an implementation detail in FRP.
To emphasize the contrast, I'd call this channel-based approach an Imperative Communicating Programming (ICP). Your code does not react to changes like it does with FRP. Instead, you as a programmer ask for changes, very often in an infinite loop, and then transform those changes into a side-effect in the browser.
On one hand, channels + goroutines provide a single abstraction with which it is kind of straightforward to implement various concurrently executing threads of code. On the other hand, those infinite loops and their proliferation in the channel-based code will definitely cause some people to cringe and go with FRP instead.
I don't think FRP is as applicable as channels to as many use cases. Channels are more low level and therefore more potent. But for what FRP does, it provides a better (read "more concise", or "declarative") abstraction for it.