Go code is very terse compared to the general run of "languages for programming in". And if you know the language, you can just read it.
On the other hand, you've got some serious digging to do if you want to understand a clojure macro. Metaprogramming like that is seriously brain twisty.
I wish this meme would stop. Your "if you know the language, you can just read it" applies just as much here as it does there.
The go version does what it does to avoid a race condition, because the goroutines are being spun up in the background and it's highly likely that it will take longer to spin up at least one of them than it will to finish the loop, so without it you'd likely get output of all nines.
If the clojure version doesn't have that problem, then I think it's a somewhat telling indication of how it's actually working.
TL/DR: The Go version has to work around the race condition because the runtime is doing things "right", is core.async?
Would it be possible (and if so what would be the simplest way) to implement something like Python's generators and `yield` statement in Clojure using core.async? I'm thinking something like:
(defn range [n]
(generator
(loop [i 0]
(yield i)
(when (< i n)
(recur (inc i)))
(let [generator (range 5)]
(generator) ;; => 0
(generator) ;; => 1
;; etc
) func generator(values ...interface{}) func() interface{} {
c := make(chan interface{}, len(values))
for _, v := range values {
c <- v
}
return func() interface{} { return <-c }
}It's also slow.
https://groups.google.com/forum/#!topic/golang-nuts/v6m86sTR...
Looks like I managed to answer the 'is it possible?' part of the question anyway -- something like this:
(defn range
[n]
(let [c (chan)]
(go
(loop [i 0]
(>! c i)
(if (< i n)
(recur (inc i))
(close! c))))
(fn [] (<!! c))))I've had generators working several times with an approach like this, but as it doesn't really fit with the rest of the library, I removed them.
All that being said, these APIs are internal and could change at any moment.
It'd be nice if the coroutine/inversion-of-control stuff was given a stable public API at some point, because those macros seem very powerful and neat in themselves and might have other interesting uses aside from channels.
From what I've read/seen, the go blocks are lightweight thread-like processes multiplexed onto a thread pool. You may be correct about using Thread/sleep, ideally I would have used (timeout ...) and then pulled off the channel. However, I didn't want to introduce the concept of channels too early in the post, so I felt Thread/sleep worked as a compromise.
We will have full API and semantic compatibility when version 0.2 is released next week. Pulsar also has cluster distribution and an Erlang-like actor framework.
Because Pulsar uses instrumentation at the bytecode level, you have more freedom within Go blocks. You wouldn't use Thread/sleep in the Pulsar implementation, either, but Strand/sleep will do the job. It detects whether you're in a go block (implemented as a fiber in Pulsar), in which case it suspends the fiber but doesn't block the thread, or within a normal thread, in which case it will simply call Thread/sleep.
(time (let [c (chan), n 1000] (dotimes [i n] (go (Thread/sleep 50) (>! c i))) (dotimes [i n] (<!! c))))
"Elapsed time: 8412.25733 msecs"
(defmacro gosleep [millis] `(<! (timeout ~millis)))
(time (let [c (chan), n 1000] (dotimes [i n] (go (gosleep 50) (>! c i))) (dotimes [i n] (<!! c))))
"Elapsed time: 91.278469 msecs"
ETA: for comparison, here's what happens if you actually make 1000 system threads:
(time (let [c (chan) n 1000] (dotimes [i n] (thread (Thread/sleep 50) (>!! c i))) (dotimes [i n] (<!! c))))
"Elapsed time: 4183.669835 msecs"
If you perform a blocking operation (Thread/sleep there, but could be, for example, a socket read?) will you run the risk of exhausting the thread pool?
The go runtime handles this by detecting how many threads are in blocking syscalls and spawning more as needed (which I guess could be undesirable).
If I've understood correctly, this is perhaps best summarised as "go makes your sync code run well in goroutines, with core.async you can block everything with sync syscalls in your go block"?
Or perhaps just "go will adjust the size of your thread pool dynamically, core.async requires it to be big enough"
This is also a good example why macros are just awesome. Go is a language design to work well with goroutins and channels, but the clojure code looks just as good and readable. You could simply not have such idiomatic use of these concepts without macros.
Or am I wrong, can a flexible language like scala or python be extended to look as good for that usecase? I dont know enougth of the tricks scala people use, I cant juge if it is possible.
But don't mistake this for having the same runtime characteristics- For what its worth the Computer Language Benchmarks Game shows Go as generally being faster, using much less memory and less code.
http://benchmarksgame.alioth.debian.org/u64q/benchmark.php?t...
Also with clojure, the actual timeconsuming calculations can be made with java and that should be at least as fast as go (with a bit more memory).
So all in all I value the architectural things much more then pure speed. Go simply has a diffrent target then Clojure.
You can find the code here: http://github.com/stuglaser/pychan
And documentation here: https://chan.readthedocs.org/en/latest/
The power of such a simple approach to editing is very hard to grasp (even more so believe) until you've actually tried it yourself.
(function arg1 arg2 ... argN)
The same impulse that made you want to run the other way should make you want to run screaming towards the language. :)
I think the easiest fix is to make the channel buffered. "make(chan string)" => "make(chan string, 1)" Not sure if there is a more idiomatic golang way to accomplish this.
rand.Seed(time.Now().UnixNano())Being a Go fan, this definitely draws me to clojure as a language since the other language I've been messing with was racket (a scheme lisp).