Hacker News new | ask | show | jobs
by codewright 4793 days ago
The beauty of concurrency in Clojure:

; Rough sketch: def defines a var (pretend it's a reference)

; @ is used to dereference the future and block to wait for the result.

  (def f 
    (future 
      (Thread/sleep 10000) (println "done") 100))

  user=> @f
  done
  100

  ;; Dereferencing again will return the already calculated value.
  => @f
  100
http://clojuredocs.org/clojure_core/clojure.core/future

Edit: And more importantly, there are wrappers for the standard data structures designed around different concurrency use-cases (sync, async, coordinated, uncoordinated)

Refs are for Coordinated Synchronous access to Many Identities".

Atoms are for Uncoordinated synchronous access to a single Identity.

Agents are for Uncoordinated asynchronous access to a single Identity.

Vars are for thread local isolated identities with a shared default value.

http://stackoverflow.com/questions/9132346/clojure-differenc...

And you can use all (all!) of the Java concurrency tooling as desired, including raw threads (for which Clojure has a wrapper as well).

Part of the reason I use Clojure rather than Go is because it doesn't try to force you into a one-size-fits-all method for handling concurrency. I have no problem with CSP but it doesn't fit everything I do. Sometimes I just want to defer work or wrap it in a future. Or I want to use an intelligent coordinated data structure rather than trying to meld flesh and bone to steel in order to make a concurrency-naive data structure behave how I want in a concurrent environ.

If I can avoid those unnecessary battles, I will.

So - Clojure.

2 comments

Go doesn't "force" you to use one method of concurrency.

It has more than one (you can do erlang-style share-nothing style or Java/C++ style of using mutexes to protect shared state from concurrent access).

I know nothing about Closure so it's possible it has more features but it's not necessarily a good thing. Is the complexity of 4 different solutions worth it? (by "it" I mean: a programmer has to learn all of them and when to use what; the implementor has to implement them; write wrappers for all standard data structures (what about third party libraries?) etc.).

Feature bloat has a cost.

Go gives you all you need to easily write concurrent programs and it does it with refreshingly simple design (both for people to learn and to implement).

So why use Go instead of node.js? You can probably take that reasoning and substitute "Go" for "Clojure", and "node.js" for "Go".

Go people would probably object that the things that Go adds, and that node.js lacks, aren't just window dressing -- sure, there are situations where a node.js-style fast event loop that avoids blocking operations is all you need, but there are also situations where you want something more like real threads, because the problem demands it.

I'm a lisper but not a Clojure expert - but I'd assume that Clojure people don't consider the existence of e.g. Actors to be "feature bloat". My impression is more that the difficult/special concurrency-enabling feature of the language is STM, and language-level support for different concurrency paradigms, implemented on top of STM, are probably low-hanging fruit once you've got it.

Static typing and availability of AOT compilation?
Futures in C++11 are similar:

    int x = std::async([]()->int{
            return 100;
        });
    std::cout << x.wait() << std::endl:
Which isn't as nice as closure. Go could probably benfit from having a standard futures tool. I guess something along the lines of:

    type Future {
        Wait() interface{}
    }

    func newFuture(func interface{}, 
                args ...interface{}) Future
Yep, futures can be done easily in Go with a goroutine that sends a single value on a channel, but: 1) you can't dereference the value multiple times in Go, so you have to manage saving it yourself, and 2) there's no syntactic sugar. Pity.
You could save the future in the Future implementation

    type future struct {
        completed   bool
        result      interface{}
        ch        <-chan interface{}
    }
Edit: Or just check if the channel is closed

    func (f *future) Wait() interface{} {
        v, ok := <- ch
        if ok { f.result = v; return v }
        else  { return f.result }
    }
I got this wrong. std::async should not return an int. It's should be std::future