35 ms·
Comparing Elixir and Go
- spraak 10y ago[Deleted] Originally I'd had an off the cuff remark here of "apples to oranges". I'd like retreat to say that the article is actually a good overview and comparison
- devmunchies 10y agoNothing wrong with comparing apples and oranges. How else would you decide which is best for any given use case?
- EduardoBautista 10y ago> It has since expanded into numerous other areas, such as web servers, and has achieved nine 9s of availability (31 milliseconds/year of downtime). I definitely want to read more about this.
- lucasmullens 10y ago"nine 9s" comes from Erlang (or in this case BEAM): https://stackoverflow.com/questions/8426897/erlangs-99-9999999-nine-nines-reliability https://stackoverflow.com/questions/8426897/erlangs-99-99999...
- kjksf 10y agoThis is how myths are created and perpetuated. This claims from a study of a particular model of Ericsson's ATM switch whose software was written in Erlang. A can tell you for sure that: * Ericsson has other switches and other telecom equipment whose software is written in C++ * other companies (Nokia, Cisco, Alcatel etc.) also built similar complex telecom equipment whose software was in C++ I'm going to bet that at least one of those devices was more reliable than that particular switch, because it's not the language that creates reliability but people writing and testing the software. Some languages make it easier than others but attributing some magic qualities from extrapolating a single case study of a telecom equipment is just silly. Telecom equipment is reliable because it has to be. If you're incapable of writing reliable software for telecom equipment then your company will go bankrupt and be replaced by one that can, even if the language they use is C++.
- planteen 10y agoYeah exactly. To make something 9 9s reliable, there is lots of fault tolerance in the system and hardware architecture.
- brightball 10y agoI grabbed it from the Erlang Wikipedia for what it's worth. The language creates reliability by isolating running parts in millions of small heaps that can independently fail and immediately restart. It's not automatic, but the way that the language is designed makes these types of numbers much more feasible. https://en.wikipedia.org/wiki/Erlang_(programming_language) https://en.wikipedia.org/wiki/Erlang_(programming_language) It also had this along the same lines though: >As Tim Bray, director of Web Technologies at Sun Microsystems, expressed in his keynote at OSCON in July 2008: "If somebody came to me and wanted to pay me a lot of money to build a large scale message handling system that really had to be up all the time, could never afford to go down for years at a time, I would unhesitatingly choose Erlang to build it in."
- lobster_johnson 10y agoBut suitability is a huge factor here. The point isn't that "nine nines" reliability cannot be achieved in C++ or whatever, it's that it's significantly harder and costlier to achieve. But also: Of course a language creates reliability. Null pointers are impossible in Erlang, whereas they are provable impossible to avoid in C++. So people bring this up because Erlang programs are pretty much reliable by default (assuming you stick to OTP and its design). To help it reach insane uptimes, Erlang also has a few features that are hard to replicate in C++, if not impossible. The main one is hot code replacement; being able to incrementally upgrade a system in a safe and controlled manner without restarting it is a huge benefit in such a system. It's also trivial to attach to a running Erlang program and introspect it as if you're truly running inside it. Goes hand in hand with hot code replacement. With C++, you get fairly crummy, low-level things like gdb, without the ability to actually write C++ code while in the debugger. Also, remember that crashes can occur in bug-free programs. Hardware failures can be hard to predict and handle.
- gpderetta 10y ago
- andyfleming 10y agoHow does Crystal lang compare to the two? I know the syntax is more similar to Elixir, but the format seems closer to Go in the sense that it compiles to a binary.
- bitwalker 10y agoMy understanding of Crystal is limited, but I think of the two, Go is the only one you would probably consider comparing it to, because of its native compilation. I don't think it has a very strong emphasis on concurrency, so comparing it to Elixir likely doesn't make much sense. Take that with a grain of salt though, as I'm not super familiar with the language.
- devmunchies 10y agoCrystal has a similar concurrency model to Go, actually. It just doesn't yet have parallelism yet (coming soon).
- andyfleming 10y agoAs far as the current interface for concurrency, it has fibers, channels, and futures.
- devmunchies 10y agoCrystal is more comparable to Go or Swift. I've been working with Crystal a lot the last few months and I'm really enjoying the emphasis on speed. The ecosystem is understandably still immature, but its going to hit 1.0 this year, so I wouldn't build anything big quite yet.
- sudhirj 10y agoSwift would be a better comparison - both Crystal and Swift are general purpose languages that compile via LLVM. Neither has any special story for concurrency (yet). Go and Elixir/Erlang on the other hand, change your approach to programming.
- brightball 10y agoAuthor here. This was published a week earlier than expected so just a heads up that there are a couple of edits coming.
- brennen 10y agoThis had the effect of making me want to spend time with both languages, so good work, I'd say.
- brightball 10y agoThat was definitely the idea. :)
- kriro 10y agoVery interesting article, thanks. I'm somewhat familiar with Elixir (learning it/have written some small things in it) and only know roughly what Go is, why it was created and I had watched a talk on it a while back. This is a great starting point for actually jumping into some Go code (at least it made me want to try it). Small typo I found: """Go channels implement buffers which can either receive a a certain number of messages""" (2x a)
- skybrian 10y agoMaybe already fixed, but here are a couple things I noticed: Go methods have to be defined in the same package as their receiver's type. As a result, you can only add methods to your own types, not someone else's. Also, the article claims that Go interfaces are similar to Elixir's pattern matching but I don't see a resemblance. Perhaps clarify that?
- brightball 10y agoBut you can define an interface that matches someone else's types and then attach the method to it. The main similarity I was going for was the way that an interface fits anything that matches it, rather than being directly implemented by the type. A method could be attached to an interface that matches a struct with two specific variables while Elixir could define a pattern on a function that worked for any struct that contained those two variables. They are both doing a similar thing a different way.
- Girlang 10y agoThey're not even in the same Universe! This is insane.
- maxpert 10y agoI think it's comparing apples with oranges. They are two different species a compiled vs VM based language, one is a functional styled vs other has duck-tapping. Only thing I can see common is GC and a somewhat but very different concurrency programming paradigms, with message passing. Erlang and BEAM is an aged old giant with battle tested proven reliability, while golang is young lad with the flash like abilities everyone has been longing for. While you get awesome stuff like Hot-Reloading, Go lang gives you compiled code that can run heavy loaded servers on my RaspberryPi (I made one and tried all Erlang, NodeJS, and Golang believe me Golang smokes them all on memory footprint http://raspchat.com http://raspchat.com ). I can go on and on and on! But picking one is totally dependent on your scenario.
- pjmlp 10y ago> They are two different species a compiled vs VM based language This is an implementation detail. Anyone can write a VM for Go, or an AOT compiler to native code for Elixir.
- timClicks 10y agoPretty significant detail though, as no one has done either.. also "anyone" is fairly strong, building decent compilers and interpreters is pretty difficult
- pjmlp 10y agoAnyone with a decent CS degree should be able to produce working compilers and interpreters, otherwise it wasn't a decent degree. Of course, writing very good ones is a different matter.
- Ralfp 10y agoYou are now commiting the moving the goalpost fallacy, arguing first that something is irrevelant because it may be changed larter, and then moving on to argue that every CS degree guy could just write it.
- aychedee 10y agoReally well written comparison that made me, as someone who has used go for a year, understand a lot more about Elixir.
- chx 10y agoI am very intrigued in both. I am a reasonably experienced software developer with about 15 hours a week of availability, anyone wants to contract me? I'd gain experience with a modern language, you'd get someone on cheaper than experience would suggest. I know software engineers can use any language but in reality, if I were to get a full time job at a company which uses either of these I'd get a lot less money than I am getting now working with Drupal with 10+ years of experience in it. So I would like to gain some real world experience to make it easier to switch jobs later.
- pdimitar 10y agoI feel this deserves its very own thread, because I too would like to do exactly the same as yourself.
- copyconstruct 10y agoThere was a good talk at Strangeloop about userland thread runtime scheduling in both the Go and the Erlang VM. https://www.youtube.com/watch?v=8g9fG7cApbc https://www.youtube.com/watch?v=8g9fG7cApbc
- julio83 10y agoThe other trade-off that comes from mutable versus immutable data comes from clustering. With Go, you have the ability to make remote procedure calls very seamlessly if you want to implement them, but because of pointers and shared memory, if you call a method on another box with an argument that references to something on your machine, it can’t be expected to function the same way. I would be happy to know how we can make RPC seamless in GO accross multiple machines (clustering). I've missed a nice lib?
- sudhirj 10y agoThe std lib has RPC support https://golang.org/pkg/net/rpc/ https://golang.org/pkg/net/rpc/ Never used it, but lots of people in conferences are happy with it.
- brightball 10y agoI might need to rework that sentence. I was trying to say that clustering can only be seamless with immutable data and that, while Go has great RPC support built in that it won't ever be able to cluster naturally.
- buckhx 10y agoThe stdlib has an RPC package, but there it also has great gRPC support http://www.grpc.io/docs/quickstart/go.html http://www.grpc.io/docs/quickstart/go.html
- Spiritus 10y agoPerhaps not exactly what you/they are referring to. But this is interesting: https://github.com/docker/libchan https://github.com/docker/libchan
- sudhirj 10y ago> The biggest difference between the two languages is that compilation for the destination architecture has to be done on that same architecture. The documents include several workarounds for this scenario, but the simplest method is to build your release within a Docker container that has the destination architecture. @brightball Go has had first class support for cross compilation for a while now, no?
- sudhirj 10y agohttp://golangcookbook.com/chapters/running/cross-compiling/ http://golangcookbook.com/chapters/running/cross-compiling/
- brightball 10y agoI need to clarify that I was talking about that being an Elixir limitation. The previous paragraph talks about Go being able to cross compile no matter what system it's on but the one you highlighted still doesn't read well.
- deleted 10y ago[deleted]
- arca_vorago 10y agoIf I were putting together a new awesome Thing(TM), I think I would probably use elixir on the front end and internal messaging to handle and distribute jobs, and let go do the crunching and hard lifting.
- pdimitar 10y agoThat's the ideal setup really. Golang's concurrency is pretty decent as well but IMO Erlang/Elixir are much easier, quicker to code in, and more reliable than anything else I've ever used. So basically use Golang as a modern C for specialized tasks which require [almost] the best your hardware can do, and use Erlang/Elixir for everything else.
- rbosinger 10y agoI know people will say "apples to oranges" but this is still exactly what I wanted to read. Thanks.
- dashoffset 10y agoAgreed. It is apples to oranges but the article does an excellent job showing when/why to use each one. I believe the conclusion was spot on and IMO combining the stability of Elixir/BEAM with the performance of Go is the best of both worlds.
- giovannibajo1 10y agoI think the part about cooperative/preemptive multitasking isn't saying it all. Go multitasking is based on the compiler inserting switchpoints on function calls and syscall boundaries. But this affects the scheduling of a single OS-level threads executing that specific goroutine. The number of OS-level threads that the Go scheduler uses can arbitrarily grow, and OS-level threads are preemptively multitasked. So I think the description is focusing a narrow view of the problem. What is usually required by applications is low latency in reply to system events (e.g.: data available on network sockets), and Go performs very well in this context. For instance, the fact that Go is transparently using a epoll/kqueue based architecture under the hood is probably affecting latency much more than the whole "cooperative" issue as depicted.
- eternalban 10y ago> I think the part about cooperative/preemptive multitasking isn't saying it all. That's still not the entire story: GC & tight loops in Go: https://github.com/golang/go/issues/10958 https://github.com/golang/go/issues/10958 Per process vs per runtime GC: https://news.ycombinator.com/item?id=12043088 https://news.ycombinator.com/item?id=12043088
- deleted 10y ago[deleted]
- eternalban 10y agoCan't update parent. Please /do not/ read my OP as a dig/boost at any level. Just pure geek interest in language architectures and sharing info.
- solidsnack9000 10y agoWhen you say "the number of threads that the Go scheduler uses can arbitrarily grow..." is it not set by GONUMPROCS or something like that? Or is it a dynamic thing -- new threads appear as needed? The cooperative issue is simply that until a thread does become free, the application can not respond to an event -- even given the epoll/kqueue architecture you describe.
- IanCal 10y agoThis is more for people looking at erlang/elixir than a critique of the blogpost or a suggestion for a change. > Within Elixir, there is no operator overloading, which can seem confusing at first if you want to use a + to concatenate two strings. In Elixir you would use <> instead. When this popped up, it reminded me of something people try to do often and then have issues with performance. You probably do not want to concatenate strings. "Yes I do" you'll first think, but actually erlang has a neat commonly used thing to help here. Let's say you're doing some templating on a web-page. You want to return "Welcome back username!". First pass (a while since I wrote erlang so forgive syntax errors): welcome(Username) -> "Welcome back " ++ Username ++ "!" Now it's going to have to construct each string, then create a new string with all three. More creation & copying means things get slower. Instead, many of the functions you'd use to write files or return things over a connection will let you pass in a list of strings instead. welcome(Username) -> ["Welcome back ", Username, "!"] Now it's not copying things, which is good. But then we want to put the welcome message into another block with their unread messages. full_greeting(Username) -> welcome(Username) ++ unread_messages() More appending than is good here, concatenating lists is going to take time. Of course, we could put it all in one function, but then we lose re-usability in the templates and have horrible massive functions. While this is a simple example, I hope you can picture a larger case where you'd want to split up the various sections. Anyway, there's a better way of doing this. The functions that take lists of strings actually take lists of strings or other lists. So we can just do this: full_greeting(Username) -> [welcome(Username), unread_messages()] You can keep going, nesting this as much as you want. This saves a lot of copying, allows you to split things up and avoids having to keep flattening a structure. So, for people about to get started, try not to concatenate your strings, you can probably save yourself and your computer some time. For more info on this, you want to search for "IO Lists" or "Deep IO Lists".
- proaralyst 10y agoThis is a similar idea to a rope: https://en.wikipedia.org/wiki/Rope_(data_structure) https://en.wikipedia.org/wiki/Rope_(data_structure)
- 10y ago
- raulk 10y agoNice writeup! ZeroMQ is not written in Erlang. You probably confused with RabbitMQ, which is.
- sahrizv 10y agoI think a good way to increase creativity and productivity is to use the right abstractions of thought and craft. Every good(non leaky) abstraction expands the creative envelope further and lends itself to creation of new higher order abstractions for the next generation. Having coded in imperative languages like Java, Python and C++, I had been on the lookout for a practical general purpose language which provides good abstractions/high expressiveness. Elixir appealed to me more than Go in that regard. It's been six months since I started writing Elixir and it's been a pleasure.
- jondubois 10y agoI still don't understand what all the hype is about pure functional programming. Sometimes mutations are useful. There are lot of good programming design patterns which depend on mutations. Also, always copying objects by value every time you call a function seems very expensive; especially if you're dealing with very large objects/structs/maps/strings which have to be processed by many functions.
- jondubois 10y agoI just had a thought; if arguments always get cloned by value every time they are passed to a function, doesn't that mean that the time complexity for an insertion operation in Elixir can never be better than O(n)? According to this answer http://stackoverflow.com/questions/11055391/time-complexity-of-erlang-dict http://stackoverflow.com/questions/11055391/time-complexity-... it claims that for dictionaries it's O(log n). How is that possible if mutations are not allowed? Wouldn't the function have to process at least n items? Or it uses parallel processing to get a speedup there?
- andy_ppp 10y agoIf I create an immutable string "hello world" why would it be passed by copy; it can never be changed only garbage collected. The BEAM seems to do magic here and do much less work than you might think when creating "new" things.
- owaislone 10y agoIf you are only dealing with Immutable structures that no one can really change, there is no point in passing by value. You can always pass by reference. The language will not allow any code to modify it. Instead a new variable will have to be assigned if the function changes the values. So, it can always be pass by reference until a change needs to be made aka copy on write.
- fetbaffe 10y agoYou only need to copy the data if the consumer of the data changes it, i.e. copy-on-write.
- nazri1 10y agoThe page (like many other pages nowadays) breaks page down - if I press the page down key I then have to scroll up a few lines because the text are obscured by the always-visible "FREE SIGNUP" banner at the top.
- manualwise 10y agoI'm responsible for this blog. Thanks for the feedback, we'll work on getting this fixed!
- julienmarie 10y agoIf you want to really understand the philosophy that makes Erlang ( and Elixir ) beautiful ( and why it made me a better programmer ), this conference by Greg Young is a kind of eye opener : https://vimeo.com/108441214 https://vimeo.com/108441214 . You realize then that clustering, hot reload, availability etc... are not only features but the logical consequence of a beautifully crafted environnement that aims at developer productivity. I'm sometimes amazed on how easy I can achieve stuff on the Erlang VM that would take ( if it's not impossible at all ) at least 10 times the time in a more usual language ( Ruby or PHP when you work in the web industry as I do ). My last example was when I needed to batch sql inserts in an events database. In a normal language I would have needed a queue, the libraries for it, workers, new deployments and infrastructure to monitor, monitoring, supervision, etc... In Elixir, in 20 lines of code, it's done. If you do not need complex calculations, the Erlang VM can basically become most of your architecture. It's already per se a SOA.
- vegabook 10y agohow easy is it, even if you do need complex calculations, to get the best of the Erlang VM and call out to say, Python/Numpy or C when necessary? Can these external processes still be supervised, for example? Are decent sized matrices (for example 100x20000 floats so an 8MB data structure) easily movable around the Erlang VM via message passing? IE is it viable in your opinion still to use Erlang as a system for distribution and routing of lots of heavy calculations to many users, if said calculations are performed outside of BEAM? I am looking at building a multivariate financial calculation engine, which must be interactive for up to 1000 users, with large firehose of real time data coming in, being massaged, and then distributed, with the calculation graph being customizable for each user interactively.
- sasa555 10y agoIt is possible to start external processes from BEAM and interact with them. I've blogged a bit about it at http://theerlangelist.com/article/outside_elixir http://theerlangelist.com/article/outside_elixir You can also write NIFs (native implemented functions) which run in BEAM process (see http://andrealeopardi.com/posts/using-c-from-elixir-with-nifs/ http://andrealeopardi.com/posts/using-c-from-elixir-with-nif...). The latter option should be the last resort though, because it can violate safety guarantees of BEAM, in particular fault-tolerance and fair scheduling. So using BEAM facing language as a "controller plane" while resorting to other languages in special cases is definitely a viable option.
- d3ckard 10y agoThat comparison was way better than I was expecting it to be. Go is still on my to-do list, but Elixir parts seem to be quite thoroughly described and without major mistakes. I also like the conclusion and to be honest this is how I always felt about the two. Definetly recommend reading this is if you're new to any of the two.
- haspok 10y agoOne thing that is completely missing from the Erlang side of the article are the OOB monitoring and operating capabilities. An Erlang VM is a living system that has a shell which you can connect to, and control both the VM and the applications running in it. You can also remotely connect to another VM, execute arbitrary code, debug, stop processes, start processes etc. It really is an operating system in itself, that was _designed_ to be that way. And the best part is that you get all this for free. Whether that is a good thing depends entirely on your needs. You probably wouldn't want to replace your bash scripts with Erlang programs :) What Erlang is not really suited for is where you need multiple levels of abstraction, such as when implementing complex business logic. You would think that the functional nature of the language lends itself to that, but then you quickly realize that because the primary concern of an Erlang engineer is to keep the system alive, and for that reason you must be able to reason and follow the code as it is running on the system, all kinds of abstractions are very much discouraged and considered bad practice (look up "parameterized modules" for an example of a feature that was _almost_ added to the language but was discarded in the end). I think that from this perspective Erlang and Go are actually very similar - both prefer simplicity over abstractions.
- julienmarie 10y agoTotally agree. Erlang is quite against "magic" which improves greatly readability. Debugging is really straightforward 99.9% of the time.
- innocentoldguy 10y agoThe article states that Elixir functions must be in a module, during its comparison between Goroutines and one of the few methods of using concurrency in Elixir (there are others beside spawn). This description isn't entirely accurate. Named functions must be inside a module in Elixir, but anonymous functions don't have that requirement.
- brightball 10y agoI realized I wasn't clear on that after the fact. I should have said defined functions. In my head I tend to think of defined functions and lambda functions as different.
- pmarreck 10y agoGo's philosophy around error handling (or lack thereof) is arguably atrocious compared to BEAM's "Let It Crash (And I'll Just Log It And Restart In 1 Millisecond With Exponential Backoff)" philosophy. To review: https://gobyexample.com/errors https://gobyexample.com/errors Manually checking every possible error (and then, only in the spots where you can imagine an error occurring) is a heck of a lot of extra work for the programmer (and code for the code reader/reviewer) and still won't catch all possible errors (both conceivable and inconceivable) properly. And arguably, the fact that an unchecked/undetected runtime bug in Go will basically send it into an "indeterminate state" which is impossible to reason about (much less debug), is an incredibly strong argument against this philosophy, IMHO. As far as I'm concerned, as soon as my code goes "off the beaten path" state-wise (read this as: "significantly differing from my mental model"), it should crash, ASAP. Isn't every bug literally a situation the programmer didn't account for? Aren't runtime errors by nature unexpected by the programmer? Why would you then give bugs and errors even more room to corrupt the state of the world, then? ;) We are all obsessed with computers and languages when the real limit is the programmer's mind and ability to reason about the code s/he's building and the states that code can get into. I think BEAM langs and purely functional langs more generally (along with functional/immutable data structures, etc.) do a much better job of addressing this root problem. I'm going to quote John Carmack from his great blog post about functional programming here (http://www.gamasutra.com/view/news/169296/Indepth_Functional_programming_in_C.php http://www.gamasutra.com/view/news/169296/Indepth_Functional...): "My pragmatic summary: A large fraction of the flaws in software development are due to programmers not fully understanding all the possible states their code may execute in. In a multithreaded environment, the lack of understanding and the resulting problems are greatly amplified, almost to the point of panic if you are paying attention. Programming in a functional style makes the state presented to your code explicit, which makes it much easier to reason about, and, in a completely pure system, makes thread race conditions impossible."
- atombender 10y agoWhile I agree wholeheartedly that Erlang's design is better: To be fair, you can't get Erlang's semantics without also inventing processes and supervisor trees. And those things would be largely meaningless without immutability. Erlang's magic comes from how all the puzzle pieces fit together into a whole: For example, with immutability comes the ability to always be able to restart from a known state, and probably greatly simplifies the internal implementation of the per-process heap. Those things fundamentally change the language; Go couldn't adopt Erlang's semantics without eschewing almost everything that makes it "Go". Error handling is really the least of it. Attempting to replicate some of Erlang's high-level design principles at a quickly lead to roadblocks. For example, goroutines cannot be killed; this means it's impossible to fully implement supervisor trees in Go. It's possible that the runtime could support this at some point, but it's a big can of worms.
- stcredzero 10y agoIn Elixir, error handling is considered “code smell.” I’ll take a second to let you read that again. I think that this makes a lot of sense. My experience in just about any language, is that the official means of error handling already feels like a code smell, even before you start using it. And if that's not the case, then it still manages to feel that way when used in a large project. Lots of Smalltalk projects would actually handle errors by saving the image on an unhandled exception. For server applications, this was like having a "live" core dump where the debugger could open up right on the errant stack frame, and you could perform experiments to aid in debugging.
- digitalzombie 10y agoFunny you mentioned smalltalk. Alan Kay was talking about how Erlang is really an OOP language more so than other languages. In term of message passing and each process is an object... Erlang just let it crash and you can restart it with a supervisor process. I think most of the time this model is much better since you won't be doing numerical stuff with BEAM anyway, it's not for it and it's too slow.
- russellbeattie 10y agoThe BEAM VM and lightweight processes seem amazing - I just wish they could be tied to a language syntax that wasn't so radically different from c/java/javascript and the like. Go code is almost immediately understandable because of this, whereas Erlang is perplexing.
- lightbyte 10y agoHave you looked at Elixir syntax? Elixir is functionally the exact same as erlang, but with a more ruby-like syntax.
- pdimitar 10y agoDefinitely check out Elixir. It's extremely easy to pick up. I also looked at Erlang but decided I don't want to learn it. I only gradually learn its stdlib because it has amazing things in there by default (like a state machine, directed graph implementation etc.). But you can use those freely from inside Elixir anytime.