7 ms·
Some thoughts on Go and Erlang
- ch4s3 12y agoPretty interesting read. I keep wanting to come back to Go. Maybe this summer.
- rdtsc 12y agoVery 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. 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. 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. 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. 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. 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.
- mamcx 12y agoTo replicate erlang, is easier if used the Actor model? Or can be done with CSP? Also, what is need to made a soft realtime language/runtime?
- rdtsc 12y agoGood questions. > To replicate erlang, is easier if used the Actor model? Or can be done with CSP? There is overlap between then two. CSP is _usually_ synchronous and Actor model is asyncronous. CSP is focused on channels. As in you send messages to a channel and the other side receives them. Channel has an identity. In Erlang you send a message to a process (its main concurrency context). A process has a process ID used to identify it. Like say you have an address at your house and and I send a letter to you. Goroutines don't have identity. You can't easily send a message to a goroutine. Kill a goroutine, see if it died to start another one and so on. > Also, what is need to made a soft realtime language/runtime? Quite simply you need one part of the system to not block other parts from making progress (returning a result). Imagine your serve a page to one user but because some other user is sent some input that take a long time to process, the first user doesn't get a response, he has to wait. Or say you have some data structures and a common heap between concurrency contexts. A garbage collector might have to stop all goroutines in order to check if it can collect any garbage. So that introduces a pause. Java Azul JVM the only VM with shared that that has concurrent and pauseles garbage collector. It is very impressive, look it up how it works. In Erlang it is easy. Processes heaps are private to each process so they can be collected independently without getting in the way.
- kator 12y ago> Java Azul JVM Oh I agree 100% here, I had a Java version of a project that went from 15k qps to 60k qps just by switching to the Azul JVM. That said I still ended up crushing that with Nginx/LuaJIT and I didn't need a proprietary JVM that wouldn't work on some systems because of kernel modules it needed to install etc.
- pron 12y ago> In Erlang it is easy. Processes heaps are private to each process so they can be collected independently without getting in the way. But that comes at the expense of throughput, and it doesn't help with shared state (ETS)
- kkowalczyk 12y agoI 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. Here's the essential difference between Go and Erlang: Go gets most of the things right, Erlang gets way too much wrong. So what does Go gets right but Erlang doesn't: * Go is fast. Erlang isn't * Go has a non-surprising, mainstream syntax. You can pick it up in hours. Erlang - not so much. * Go has a great, consistent, modern, bug-free standard library. Erlang - not so much. * Go is good at strings. Erlang - not so much. * Go has the obvious data structures: structs and hash tables. Erlang - no. * 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 * 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 So sure, if you ignore all the major faults of Erlang (http://damienkatz.net/2008/03/what_sucks_abou.html http://damienkatz.net/2008/03/what_sucks_abou.html, http://www.unlimitednovelty.com/2011/07/trouble-with-erlang-or-erlang-is-ghetto.html http://www.unlimitednovelty.com/2011/07/trouble-with-erlang-..., http://ferd.ca/an-open-letter-to-the-erlang-beginner-or-onlooker.html http://ferd.ca/an-open-letter-to-the-erlang-beginner-or-onlo..., http://sacharya.com/tag/erlang-sucks/ http://sacharya.com/tag/erlang-sucks/) it compares very favorably to Go. 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.
- pcwalton 12y ago> 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 There are approaches that allow the flexibility of shared state without the possibility of lurking data races or, worse (in Go's case) lack of memory safety. Even JavaScript has such a solution now (Transferable Objects). In fact, Erlang itself has one such approach: ETS. To be honest, I don't think unrestricted shared state is the right thing in a programming language. It just invites too many bugs (and race detectors don't catch enough of them).
- cnbuff410 12y agoIs 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". For example, he said "But when it comes to complex backends that need to be fault-tolerant Go is as broken as any other language with shared state." Why? Why shared state is so bad in Go? Isn't it taken care by Go's channel anyway? Also, why Pre-emptive Scheduling is bad? Isn't Error Handling still pretty much just a matter of personal preference? Why Introspection makes Erlang so much better? What's the practical key problem for Go that can not be tackled without instrospection? And I completely failed to understand the point of "Static Linking". 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
- pcwalton 12y ago> Why? Why shared state is so bad? Isn't it taken care by Go's channel anyway? Because you can have data races and memory safety issues.
- riobard 12y agoShared state is bad because it breaks local reasoning. Without enforcing certain disciplines (i.e. always sharing by passing messages via Go channels) it's hard, if not impossible, to reason about local behaviors without considering other parts of the code. Immutable vs shared mutable state is a design choice. Erlang chose immutability for safety, while Go chose shared mutable state for practical reasons, but as a remedy Go recommends best practices to avoid the drawbacks caused by it. Pre-emptive scheduling is bad because you can have a goroutine running a tight loop starves other goroutines. To address that problem, you should manually call runtime.Gosched() to yield control and allow the scheduler to run other goroutines. Erlang's reduction-based scheduling does not have this problem and can be very fair. Goroutine lacking identity is a major design difference from Erlang. In Go, channels have identity, but goroutines don't. In Erlang, it's the other way around: processes have identity, and channels are implicit a.l.a mailboxes. In theory you can simulate one style in the other, but the implication of this design choice is very proud. I'm a Go fan, but personally I think Erlang's model is easier to reason about in scale, and it has this nice symmetry with OS threads/processes (you can kill them easily. Good luck killing a goroutine).
- natural219 12y agoThe 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. 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." Seriously, this is the canonical debugging advice, from Rob Pike himself: "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 how* that problem could happen, he'd intuit where the model was wrong or where our code must not be satisfying the model."*[1] [1] http://www.informit.com/articles/article.aspx?p=1941206 http://www.informit.com/articles/article.aspx?p=1941206
- thinkpad20 12y agoI hope that wasn't the actual reason why they decided against a REPL or other tools, because unfortunately, most of us aren't as smart as Ken.
- courtf 12y agoHonestly not trying to be a brat here, but I do this a lot myself, and I'm no Ken. Sometimes sitting back and just examining the symptoms of a bug can help narrow down the context that it was likely to have arose from (if you know the code base well enough). I often find that firing up the debugger tends to lead down tangential rabbit holes, particularly when dealing with a heavy framework. That said, a REPL would be nice. Seems like go compiles fast enough that some ambitious dork could make a REPL-faker?
- signa11 12y agoimho, 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 optimal way to do concurrency is with shared state, but that gets exported out via channels and go-routines etc. also, can someone please explain the issues around 'nil' ? i fail to appreciate author's concern about those...
- steveklabnik 12y ago> also, can someone please explain the issues around 'nil' ? i fail to appreciate author's concern about those... Take it from the guy who _invented_ nil: http://qconlondon.com/london-2009/presentation/Null+References:+The+Billion+Dollar+Mistake http://qconlondon.com/london-2009/presentation/Null+Referenc...
- stock_toaster 12y agoIt should be noted that nil in Go is not quite[1] the same as null in C though. For example, if a method defines the return type as a value (say, a string), you cannot validly return a nil (compile error). [1]: http://blog.denevell.org/golang-null-nil.html
- masklinn 12y agoEr… similar code in C: struct foo bar () { return NULL; } is a compilation error as well… (granted it's not an error for strings because C has no strings per-se)
- stock_toaster 12y ago> (granted it's not an error for strings because C has no strings per-se) That was kind of the entire point though.
- masklinn 12y ago
- shmerl 12y ago> his biggest surprise was Go is mostly gaining developers from Python and Ruby, not C++ 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.
- cshesse 12y agoHe was probably surprised because they were trying to make a better C++, not a better Python.
- rsynnott 12y agoI didn't actually realise this until it was pointed out; if nothing else, mandatory automatic memory management really hurts its usefulness for a lot of the things that people still use C++ for.
- dagw 12y agoWhy is that a surprise? Because it was never the intention. Pike and Thompson, by their own admission, hated C++ and set out to write a better language to replace it. Python and Ruby developers where never on their radar during the whole design phase. It might be logical and obvious in hindsight, but it certainly came as a surprise to the creators of the language.
- shmerl 12y agoI see. I guess they misjudged features which C++ developers actually valued as something bad and in need of fixing. I.e. while trying to improve, they removed something that was actually good (such as replacing RAII with GC). And on the other hand not fixing real fundamental problems (such as concurrency safety for example). This probably made the language less attractive as a C++ replacement, but still attractive for those who didn't look to replace C++.
- aufreak3 12y agoA 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. 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? 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." 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.
- jeffdavis 12y agoAgreed. While we're at it, "the right tool for the job" always struck me as a bad analogy. Nobody would question a carpenter's tool choices because the only thing that affects others is the quality of the final result (e.g. how the building will stand up to stress). But using a software language, framework, library, database, OS, or other platform makes it inextricable from the the rest of the product when judging quality. In some cases, you can make a black box argument like "if a user is unable to observe any poor qualities, then the product must be of high quality"; but that only really applies when you are the sole developer and always will be. For larger projects, there are others involved, and they will be affected if the building blocks are poor. Granted, that doesn't mean that all discussions about platforms are productive, but it means there is some room for illumination and progress.
- davidw 12y agoAdditionally, for many people, the "right tool" is the language they know best, even if it's not 100% the best at any given task, because it's better to get some working code rather than stop, learn a new language, build something with it that sucks, redo it, and so on. There are exceptions to this of course, sometimes something really is inadequate, but for many people a general-purpose language is going to be "good enough". I'm an Erlang fan, but I do think this sometimes hurts its adoption.
- callmeed 12y agoGreat article. I'm curious how people are deploying Go apps in production. Is it nginx+some app server? (In other words what's to Go equivalent of nginx+unicorn or apache+passenger in the RoR world?)
- patrickg 12y agoI have an apache + reverse proxy for go servers running and listening on their specific port.
- jamra 12y agoI use nginx + upstart to keep my golang process up and running. When I asked about it on the golang irc channel, people said that they just use golang without a reverse proxy. I personally like the ssl termination that nginx offers. I believe that nginx is a better way to load balance and I like not having to run my code as root, which using a reverse proxy provides you.
- justincormack 12y agoYou can use capabilities to allow binding to port 80 without being root.
- jamra 12y agoI've heard that before but never got around to trying it out. I should really take a look. Thanks.
- vertex-four 12y agoAnother alternative is authbind[0], which doesn't have the same drawbacks (i.e. you can use it with scripts, and still set LD_LIBRARY_PATH). [0] http://en.wikipedia.org/wiki/Authbind http://en.wikipedia.org/wiki/Authbind
- jeffdavis 12y agoErlang 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. 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. I haven't used erlang in production, but I've invested some time to learn about it because I see that potential value. 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. 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. Go should have been another cluster language/VM that really could compete with erlang, in my opinion. To me, Go is just another language. [1] Rust does seem to have something to offer here.
- cordite 12y agoAs mentioned in the post and having basic experience with go, go doesn't solve my problems and it's presence of nil makes it feel like a fancy C. Rust does have something to offer and I've helped a bit with a thing called oxidize (not the compilation phase in rustc) as an attempt to make a web framework (or at least a routing layer on top of an http lib from the community) I wonder how this author would feel to compare Erlang and Rust once it matures.
- jaegerpicker 12y agoJust because Go doesn't fit your use case doesn't mean it should be something else. In most cases Go IS a better choice than C/C++ in a server side case. For instance let's say you are writing a server for a multiplayer game. Most of the server's activity is spent on waiting for IO and managing concurrent socket based connections. There are drawbacks and risks to managing memory in this type of system and Go's concurrency makes writing this code much easier. Another great go use case is a rest based web service, a lot of these are typically written in Java, Ruby, or Python. I love python but go has serious advantages over all three of those languages in this use case. This is where go was designed to be used and it's best spot. The glue servers that are most commonly used for web applications. Go is designed to be simpler, safer, and easier to write than C/C++, Java, or C# but faster, more modern, and more fault tolerant than python or ruby. It's not a perfect language by any means but it does fill a nice sweet spot.
- kator 12y agoInteresting read, I have recently been porting a very low latency high scale project of mine from Nginx/LuaJIT to Go just to learn Go. 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. 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. 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. 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.
- personZ 12y agoI 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. This sounds somewhat incredible and unlikely to be a result of Go, more likely to be a facet of a naive implement (we can all break any language). I've built a number of extremely high capacity/concurrency systems in Go to great success, as have many other very large organizations, so the notion that it's just fundamentally immature or broken doesn't fly. All of the talk about GC in Go is a bit curious, because Go actually makes very little use of GC -- it very heavily favors the stack, versus many other platforms (.NET, Java, others) that use the heap for virtually everything, and turn most everything into an object. The simple fact that Go has a GC doesn't mean that its GC use is the same as all other languages that use a GC.
- JulianMorrison 12y agoYou can "link" goroutines in Go by using defer func(){ if recover() != nil { // tell my parent I died } }() and, Go relies on channels, not goroutines, having identity.
- kungfooguru 12y agoI've added an update to the top of the post because I didn't make the point clear enough: 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.
- zimbatm 12y agoNot 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.
- erichocean 12y agoFrom a particular perspective, that's what Docker is trying to do (or at least, is providing infrastructure to build something like an "OTP for Docker" with).
- deleted 12y ago[deleted]
- rdtsc 12y agoThis is not a 16 year old kid but the lead engineer of Heroku. > Go is something like a simplified C Not is it not. Go is a shitty C. Try writing a kernel and Go and see what happens.
- stcredzero 12y agoOh, whoops. I posted a comment to the wrong thread! (There, the op is actually a 16 year old who is surprised Haskell is a better Haskell than Go.) Try writing a kernel and Go and see what happens. It's not that kind of language. A kernel written in Haskell would be very interesting, however.