Erlang is designed to accommodate principles for building reliable systems in the presence of software errors, and in such capacity it is definitely a worthy instrument. It is one of the few concurrency oriented programming languages in the world.
But as Joe Armstrong himself pointed out in his dissertation: "indeed concurrent programs can be written in languages which are not themselves concurrent". You can write concurrent programs in Python if you want. Or even shell. As soon as you can spawn a process and send it a message - you're good.
And this explains why Erlang hasn't conquered the world. As soon as it has established the principles, as soon as it proved them feasible in practice, it has made itself redundant. The lessons we learned from Erlang were larger than Erlang itself.
> As soon as it has established the principles, as soon as it proved them feasible in practice, it has made itself redundant.
Features such as the memory model and lightweight processes are intrinsic to the BEAM VM and aspects of the language (such as immutability). Erlang is a mixture of no memory management, fail-fast and embarrassingly parallel without GC pauses, and getting those characteristics in another language can’t be had without fundamental and breaking changes, to the point of becoming Erlang, which seems awfully redundant.
I strongly disagree that we learned much of anything.
For example: hot upgrading. The fact that an Erlang process can tail call itself means that you can swap in a new process with the same signature at the tail call of the old process. Nobody else does this that I know of.
I can go on further. The whole set of behaviors encoded by the OTP libraries is also something that nobody else seems to do. Erlang's bit syntax is possibly the best out there even today.
I guess pattern matching finally made it to other languages. I would think that's probably about it from Erlang (pattern matching isn't exclusive to Erlang but it was one of the earlier languages with it).
None of the other programming languages seem to have learned much of anything from Erlang--and that's kind of sad.
> For example: hot upgrading. The fact that an Erlang process can tail call itself means that you can swap in a new process with the same signature at the tail call of the old process. Nobody else does this that I know of
common lisp is in a league of its own when it comes to hot reloading and debugging
Question is if Common Lisp got the idea from Erlang, or vice-versa. Common Lisp first appeared in 1984 while Erlang appeared in 1986. If so, the original point of "None of the other programming languages seem to have learned much of anything from Erlang" might remain accurate.
In the 70s Ericsson programmed their telephone switches in a proprietary language called PLEX. It had hot code swapping, so when Joe Armstrong started working on Erlang to replace PLEX in the 80s this was a requirement. Dropping a few thousands of calls just to do an update simply wasn't an option.
On the other hand, even the first versions of lisps (as far as I can gather at least) had `eval`, meaning a running program could accept external output and update itself. And this was in the 60s.
Lisp would also be able to compile code to assembler, run an assembler and load the generated code into the runtime.
I would think (without knowing too much about Erlang's mechanisms) that the mechanisms of Erlang are quite different from what a Lisp runtime typically does. The Lisp runtime is just one process. Erlang is concerned with multiple processes, which are strongly isolated.
Hot upgrading is not a good example. There are infinity ways to do accomplish the same in other ecosystems, a very common example:
Upgrade nginx live and without dropping any connections.
Anyhow, instead of bolting that on to your project reinventing those patterns for the millionth time you can just adhere to ̶the c̶o̶n̶j̶o̶i̶n̶e̶d̶ ̶t̶r̶i̶a̶n̶g̶l̶e̶s̶ ̶o̶f̶ ̶s̶u̶c̶c̶e̶s̶s̶ the tenants of 12 factor apps: https://12factor.net/disposability
> None of the other programming languages seem to have learned much of anything from Erlang--and that's kind of sad.
Learn you some very basic devops and you can collect on those erlang-y benefits using whatever languages/tooling/paradigms you want. A lot of it is flat out built into and managed by ye cloud providers.
Infra people squint at something like Beam/OTP and see a "control plane".
> Upgrade nginx live and without dropping any connections.
You can do that, but you won't get the new behavior on the existing connections, only for new connections. Some http servers will also curtail keep-alive requests while draining the old server processes, so you still get to pay the cost of reestablishing long lived connections, if your use case includes those.
Erlang was originally either implemented in Prolog or was even a library / extension for Prolog, and its pattern matching functionality is just "because it was all Prolog and the developers were using Prolog", so it isn't really fair to give Erlang any credit at all for that as it wasn't even independently designed.
> For example: hot upgrading. The fact that an Erlang process can tail call itself means that you can swap in a new process with the same signature at the tail call of the old process. Nobody else does this that I know of.
You can do hot loading in C with dlopen and friends, but you've got to be a lot more intentional, and have the right shape of program. I did it once for a networking proxy type thing where Erlang was inconvenient (I'd need to make NIFs to get at the data and didn't want to deal with that), but I had state I didn't want to lose or serialize while making changes to the code. Of course, I then proceded to make only one or two changes. ;)
Edit: or you can do it in C the more exciting way, by updating your mmaped libraries on disk. That'd take a lot of intentional effort to not just crash, which is what tends to happen when you accidentally update instead of replace mmaped libraries. (There's a reason to use install instead of cp)
Very technologically cool, but I'd much rather inject the new functionality into the source code. From there it can it make its way through the unit tests, integration tests, deployment, health/readiness checks, and if all that passes then I'll allow it to be called.
Sure but that is just one example. There are lots of ways to roll your own similar setup on your own hardware. If you don't like Fargate you can go the k8s route, avoiding lock in is the whole point of it.
> Very technologically cool, but I'd much rather inject the new functionality into the source code.
Swapping in a new process and injecting the new functionality into the source code are not mutually exclusive. You can do both!
Erlang programmers are not idiots who would swap in new processes live without writing the source code for it and testing it. They write the code, the unit tests, the integration tests, deployment, health/readiness checks, and if all that passes then the deployment tools take care of swapping in the new process with zero downtime.
> Very technologically cool, but I'd much rather inject the new functionality into the source code. From there it can it make its way through the unit tests, integration tests, deployment, health/readiness checks, and if all that passes then I'll allow it to be called.
Generally, you do make the changes in source code, compile it, do all your pre-flight checks, and then hotload it. Honestly, sometimes the pre-flight check is just 'does it compile', but that's philisophical; you can put whatever process in place you want. But from my experience, it's much nicer to upgrade in place than start new, move traffic, drain old (not always in exactly that order). Sometimes, it's not realistic to avoid starting new and moving traffic, but it's nice when you can.
Most people aren't patching the VM bytecode by hand (to my knowledge).
> And this explains why Erlang hasn't conquered the world.
I suspect the reasons are less technical than marketing, timing, etc. It takes some experience to appreciate what it does - it's not an obvious sell like "get a webapp going in 30 min" can be for Ruby. Although Elixir/Phoenix are doing some amazing things to fix that :)
OS processes are a fine way to isolate concurrent processes if they're quite granular. It works less well when there's 10000 of them (e.g. websocket connections), and they want to share a DB connection pool, metrics, etc.
> As soon as it has established the principles, as soon as it proved them feasible in practice, it has made itself redundant. The lessons we learned from Erlang were larger than Erlang
I made a blog post around this very idea, that new languages never get the adoption that they need, because existing languages simply incorporate the best ideas from the new languages.
At which point, why will anyone change the the new language if the features that are important have been fitted into their current languages.
Those languages tend to die relatively quickly (zoomed out on a timescale, obviously), languages like CoffeeScript. When it first appeared, it was a god-send, but the good ideas were integrated into the core language, and itself went on to "die".
But then you have languages that have features that you cannot replicate to other languages. You might be able to add arrow functions from CoffeeScript to JavaScript, but you won't be able to tack on homoiconicity from lisps (and macros) to JavaScript, because it simply won't match with how JavaScript is written.
I guess my own conclusion is that languages that don't really invent much can be useful in the short term (like TypeScript), they go on to die when best ideas are integrated, but languages that are really inventive (like lisp-derivatives) end up on a different branch where ideas simply cannot be borrowed to other languages, so they stay around for the long haul.
> But then you have languages that have features that you cannot replicate to other languages. You might be able to add arrow functions from CoffeeScript to JavaScript, but you won't be able to tack on homoiconicity from lisps (and macros) to JavaScript, because it simply won't match with how JavaScript is written.
Yeah, but I specified "best features" and "important features", not "all features".
Maybe homoiconicity simply isn't that important to developers?
For example, I can guarantee you that, right now in todays development climate, there is no team anywhere that is going to allow code which has Lisp-like self-modifiying ASTs to pass code review[1].
So why would Javascript, C#, Java, etc developers bother to a) request those features, and b) implement those requests? They aren't going to use the feature anyway, no matter how powerful.
[1] Except those places who already use Lisp, because they presumably already understand it, and those teams are tiny.
But as Joe Armstrong himself pointed out in his dissertation: "indeed concurrent programs can be written in languages which are not themselves concurrent". You can write concurrent programs in Python if you want. Or even shell. As soon as you can spawn a process and send it a message - you're good.
And this explains why Erlang hasn't conquered the world. As soon as it has established the principles, as soon as it proved them feasible in practice, it has made itself redundant. The lessons we learned from Erlang were larger than Erlang itself.