Communicating Sequential Processes
swannodette.github.io
swannodette.github.io
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.
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.
EDIT: Oops, I didn't see you had linked to the reflect features. I did not know that and that is very cool.
reflect.Select does exactly that.
http://thinkrelevance.com/blog/2013/07/10/rich-hickey-and-co...
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.
This is an example of Rock Paper Scissors by Alex Miller (@puredanger): http://tech.puredanger.com/2013/07/10/rps-core-async/
I'm quite curious about what the compiled code looks like. (On a mobile now, I'll have a look when I can.)
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.
Here's an example.
I wonder if most of that is the runtime and libraries or the resulting JavaScript code is just that big.
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.
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).
* Intro: http://clojure-doc.org/
* Libs: http://www.clojure-toolbox.com/
* Examples: http://clojuredocs.org/quickref/Clojure%20Core
* Pedestal: https://github.com/pedestal/app-tutorial
Also a few books at different levels:
* (2nd ed early access available) http://joyofclojure.com/
* http://pragprog.com/book/shcloj2/programming-clojure
* http://www.manning.com/rathore2/
* http://www.packtpub.com/clojure-data-analysis-cookbook/book
(On the whole, a really excellent set of books for the language)
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.
[1] https://github.com/clojure/core.async
[2] http://brehaut.net/blog/2011/ring_introduction
They may have fixed that recently, and this CSP implementation is really cool, so I may take a look back at the language.
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.
Dart fails to address concurrency? I don't think so: http://www.dartlang.org/docs/dart-up-and-running/contents/ch...
"Isolates send messages using SendPorts, and receive them using ReceivePorts. The content of a message can be any of the following:
* (...)
* An instance of SendPort"
You can prefer one or the other model, but saying that Dart fails to address concurrency is just not true.
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."
See: http://clojure.com/blog/2013/06/28/clojure-core-async-channe...
It should be noted that actors better fit in a distributed system, which is a step above and beyond concurrent systems.
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
https://gist.github.com/mraleph/5991990
you can see that it is not that much different from ClojureScript versions.
I also doubt anything large will happen to the language until after 1.0
Right now there are two separate issues in the Dart's bugtracker:
one for Futures https://code.google.com/p/dart/issues/detail?id=104 one for Streams https://code.google.com/p/dart/issues/detail?id=7002
but none contain any details of how the syntax will look like at the end.
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.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.
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.
> 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.
The security implications of channels are neat too, you can treat them as capabilities to specific parts of a process' API.
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.
And here's a todo's example that I wrote https://github.com/bhauman/async-explore/blob/master/src/tod...
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.
[1]: https://github.com/clojure/core.async/blob/bb77337b3e9ce2118...
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.
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.
As for language features? core.async is not a feature of Clojure/ClojureScript; it's merely a library.