Very good comparison, stuff that most other blogs don't talk much about -- scheduling, fault isolation, garbage collection strategies. I guess they don't because other frameworks/languages don't provide that. It usually stays at syntax level with obligatory mention of generics.<p>Fault isolation and pauseless garbage collection is something that is very important in some contexts. Often the need for it becomes apparent the second time around, after one version of the system has been plagued by large mutable shared state bugs, or strange, un-predictable response times in a highly concurrent system.<p>Do you pay in terms of raw CPU performance by copying messages and keeping private heaps per lightweight process? Yes you do. There are no magic unicorns behind the scene. That is the trade-off you get for getting fault isolation and soft realtime properties. But keep this in mind, one of the biggest slowdowns a system incurs is when it goes from working to crashing and not working.<p>Also, no matter how strong the static compile checking is, your system will still crash in production. It is usually a hard bug that has been lurking around not a simple "I thought it was an int but got a string", those are caught early on. It will probably be something subtle and hard to reproduce. Can your system tolerate that kind of a crash in a graceful way? Sometimes you need that.<p>In the end, it is good for both systems to be around. If you need fault tolerance, optimization for low latency responses, supervision strategies, built-in inter-node clustering(node = OS process or instance of running Erlang BEAM VM) you cannot get that in any other way easily.<p>Now, one could think of trying to replicate some of the things Erlang provides. Like say build tools static analysis tools to check if go-routines end up accessing shared state. Or say, devise a strategy to use channels-based supervision tree. Heck, if you don't have too many concurrency contexts (processes, go-routines) you can always fall back on OS process-based isolation and use IPC (+ ZMQ for example), as a mailbox. But then again Erlang provides that in one package.
I've added an update to the top of the post because I didn't make the point clear enough:<p>I’m seeing that I did not make the point of this post clear. I am not saying Go is wrong or should change because it isn’t like Erlang. What I am attempting to show is the choices Go made that make it not an alternative to Erlang for backends where availability and low latency for high numbers of concurrent requests is a requirement. And notice I’m not writing this about a language like Julia. I have heard Go pitched as an alternative to Erlang for not only new projects but replacing old. No one would say the same for Julia, but Go and Node.js are seen by some as friendlier alternatives. And no, Erlang isn’t the solution for everything! But this is specifically about where Erlang is appropriate and Go is lacking.
Erlang is a language with a purpose that I can relate to. I develop a cluster database product, and erlang seems to have an answer for many problems that I have actually faced.<p>For instance: cluster global pids. When you send a message to a pid, you don't have to go through a dance of handling errors and timeouts just because it might live on another node. If the node goes down, there are several ways to handle that, including monitor_node() which gives you a message that you can handle in one place.<p>I haven't used erlang in production, but I've invested some time to learn about it because I see that potential value.<p>I don't really see that from Go. It seems to fall into a "general-purpose" bucket where it competes with Java, C#, python, ruby, haskell, clojure, scala, etc. I don't necessarily like all of those languages, and for any given one you can probably pick out some Go advantages. A lot of people say Go hits a nice sweet spot for many applications.<p>But Go just doesn't speak to any problems I actually have. It can't replace the C/C++ code, because Go has a GC, and the code is C/C++ because it needs to manage memory[1]. It could replace the python code, perhaps to some benefit, but there would still be a bunch of awkward communication between processes and all of the associated error handling mess. And it can't replace Java, because that's a public API we offer, and lots of people are comfortable with Java.<p>Go should have been another cluster language/VM that really could compete with erlang, in my opinion. To me, Go is just another language.<p>[1] Rust does seem to have something to offer here.
A meta comment - I'm reading a lot of the "use the right tool for the job and stop arguing" statement in language/framework/system/machine comparison threads these days.<p>I find the "right tool for the job" to be a total conversation stopper. It stops the bikeshedding type arguments, true, but it also stops potentially illuminating comparisons. Can we, as a community, agree to stop bringing it up in comparison arguments?<p>Imagine a new programming language and system being presented in an article. It is healthy and useful for the article to say "We designed system X so it is easier to express Y kind of programs. A, B, C are the complications encountered when doing the same with systems I, J, K." rather than "We designed system X to express Y kind of programs. We like it, but if you don't, use the right tool for your job."<p>While many of us are polyglots, we do seek to minimize the number of parts when building a system, so such comparisons are often meaningful at some level.
Is it just me or the author fail to explain all the Go's detrimental design clearly? Most of the points he listed there are pretty much personal taste thing and basically what he was saying is "Go has so many problems because Go is not designed as Erlang".<p>For example, he said<p>"But when it comes to complex backends that need to be fault-tolerant Go is as broken as any other language with shared state."<p>Why? Why shared state is so bad in Go? Isn't it taken care by Go's channel anyway?<p>Also, why Pre-emptive Scheduling is bad? Isn't Error Handling still pretty much just a matter of personal preference?<p>Why Introspection makes Erlang so much better? What's the practical key problem for Go that can not be tackled without instrospection?<p>And I completely failed to understand the point of "Static Linking".<p>I'm not trolling. I don't have Erlang experience, and most of the problem the author pointed out was not bothering me, so I honestly want to see WHY they are problematic in Go
Interesting read, I have recently been porting a very low latency high scale project of mine from Nginx/LuaJIT to Go just to learn Go.<p>I have already right off the bat ran into the concurrency issues even with Go 1.3beta. And the GC locking causes all my connections to drop and thus causes thrashing.<p>That said I've coded in many languages in my 30 years of development while often reverting back to C over and over again as I've needed the lower level solutions to some big problems. I've enjoyed learning Go and plan to continue because it just "feels right". It's hard to explain but I can see it useful for numerous problem sets and I don't have to dive into C++/Java again to get convenient memory management and hopefully I'll never look pthreads in the face again.<p>However, I have really been amazed at the speed of LuaJIT. If you would have told me the fastest toolset I could use for my low latency high scale project was an "interpreted language" I would have laughed you out of the room. I did try Python (Cython) and Java and numerous other tools. But so far LuaJIT has turned out to be the fastest, not the most elegant to read but coder time -vs- return on that time is the highest thus far.<p>I am hopeful that Go will mature in the runtime in ways that will make it compete with Nginx for it's amazing event driven non-blocking architecture. With that in mind I think writing many things in Go will be useful and improvements of the underlying tech will just be magnified by all the code that now needs just a simple recompile to capture those changes. It's like the old days of gcc when I found hand coding some ASM was more useful and now a days it kicks out some amazing code with little need for inlines except in the most extreme situations. Here's to hoping Go traverses that path faster then gcc did and we all will have a more enjoyable time solving the problems we love to solve every day.
<i>> his biggest surprise was Go is mostly gaining developers from Python and Ruby, not C++</i><p>Why is that a surprise? I think it's logical, and Go can be expected to attract developers who are used to garbage collection (Java, Python, Ruby etc.). Not so much C++ developers who prefer to have control and choice (i.e. pay for what you choose, rather than get it handed down forcefully). Rust is a better candidate for attracting more C++ developers than those from Java, Python and Ruby background.
The biggest turn-off about Go, for me, is that the community seems to be incredibly unfriendly to newcomers. He touched on the lack of REPL, but it goes further than that.<p>For instance, there is very little in the toolchain about debugging other than "use GDB". For someone very familiar with the workflow of typing "debugger" in Javascript code, being able to stop the world at any state, examine variables, and having a fully-functional REPL to test expressions, Go's way of "debugging" code is...well. I don't really know how to do it in Go. The general answer seems to be something along the lines of "think about the code you wrote and then write it correctly you idiot."<p>Seriously, this is the canonical debugging advice, from Rob Pike himself:<p><i>"When something went wrong, I'd reflexively start to dig in to the problem, examining stack traces, sticking in print statements, invoking a debugger, and so on. But Ken would just stand and think, ignoring me and the code we'd just written. After a while I noticed a pattern: Ken would often understand the problem before I would, and would suddenly announce, "I know what's wrong." He was usually correct. I realized that Ken was building a mental model of the code and when something broke it was an error in the model. By thinking about </i>how* that problem could happen, he'd intuit where the model was wrong or where our code must not be satisfying the model."*[1]<p>[1] <a href="http://www.informit.com/articles/article.aspx?p=1941206" rel="nofollow">http://www.informit.com/articles/article.aspx?p=1941206</a>
imho, go, afaik, is designed to cleanly express concurrency primitives in the context of a single system, and doesn't do 'fault-tolerance' in the erlang sense of 'you need >1 machine to be fault-tolerant'. with that lens, it is clear that the <i>optimal</i> way to do concurrency is with shared state, but that gets exported out via channels and go-routines etc.<p>also, can someone please explain the issues around 'nil' ? i fail to appreciate author's concern about those...
Great article.<p>I'm curious how people are deploying Go apps in production. Is it nginx+some app server?<p>(In other words what's to Go equivalent of nginx+unicorn or apache+passenger in the RoR world?)
I write it fully acknowledging that programming language flamewars are pointless, but this article just shows that you don't even have to try hard to create a biased comparison.<p>Here's the essential difference between Go and Erlang: Go gets most of the things right, Erlang gets way too much wrong.<p>So what does Go gets right but Erlang doesn't:<p>* Go is fast. Erlang isn't<p>* Go has a non-surprising, mainstream syntax. You can pick it up in hours. Erlang - not so much.<p>* Go has a great, consistent, modern, bug-free standard library. Erlang - not so much.<p>* Go is good at strings. Erlang - not so much.<p>* Go has the obvious data structures: structs and hash tables. Erlang - no.<p>* Go is a general purpose language. Erlang was designed for a specific notion of fault tolerance - one that isn't actually needed or useful for 90% of the software but every program has to pay the costs<p>* Go has shared memory. Yes, that's a feature. It allows things to go fast. Purity of not sharing state between threads sounds good in theory until you need concurrency and get bitten by the cost of awkwardness of having to copy values between concurrent processes<p>So sure, if you ignore all the major faults of Erlang (<a href="http://damienkatz.net/2008/03/what_sucks_abou.html" rel="nofollow">http://damienkatz.net/2008/03/what_sucks_abou.html</a>, <a href="http://www.unlimitednovelty.com/2011/07/trouble-with-erlang-or-erlang-is-ghetto.html" rel="nofollow">http://www.unlimitednovelty.com/2011/07/trouble-with-erlang-...</a>, <a href="http://ferd.ca/an-open-letter-to-the-erlang-beginner-or-onlooker.html" rel="nofollow">http://ferd.ca/an-open-letter-to-the-erlang-beginner-or-onlo...</a>, <a href="http://sacharya.com/tag/erlang-sucks/" rel="nofollow">http://sacharya.com/tag/erlang-sucks/</a>) it compares very favorably to Go.<p>You just have to overlook ugly syntax, lack of string type, lack of structs, lack of hash tables, slow execution time. Other than those fundamental things, Erlang is great.
You can "link" goroutines in Go by using<p><pre><code> defer func(){
if recover() != nil {
// tell my parent I died
}
}()
</code></pre>
and, Go relies on channels, not goroutines, having identity.
Not directly related but I'm wondering when or if it's even prossible to implement OTP on the system level. OTP is basically a process manager with linked dependencies ? I know unix processes aren't as lightweight but it would still be useful I think. It's the right level of granularity for languages who have shared mutable state internally.