3 ms·
It's a functional, concurrent-first language. People have talked about the upsides of functional programming to death, either you prefer it or you don't. The co
by OkayPhysicist 3y ago
It's a functional, concurrent-first language. People have talked about the upsides of functional programming to death, either you prefer it or you don't. The concurrency story, though, is a significant advantage exclusive to the BEAM languages. They utilize green threads with message passing queues that we call "processes" (not to be confused with OS-level processes).
A good almost-toy level project I built in Elixir was a Minecraft server proxy. At the top level, you've got a listener process, that waits for an incoming TCP connection, and a Supervisor process, who's responsible for the existing connections. When the listener receives a new TCP connection, it spawns a process under the Supervisor to handle that incoming connection, and goes back to listening. That's about 10 lines for code: 4 in the initiation (function declaration, start Supervisor, start Listener, end), and 6 in the Listener (function declaration, get new TCP connection, spawn process as a child of the Supervisor, assign TCP connection, recurse, end)
The child processes are running a little state machine to do some parsing of the TCP stream. They receive TCP packets as a series of messages, and based on those messages they do various things. Unimportant for the purposes of explanation, except to say that the built-in gen-statem state machine handler simplifies it down to a series of functions that match a message, and produce a new state.
But what if I receive a packet that I don't have a defined response to? Well, I don't need to write any extra code for that. When it fails to pattern match any of my states, the process is going to crash. Then it's children will crash (including the tcp connection, which will be closed by this operation), then it'll tell it's Supervisor "Alas, I am slain". By default, the Supervisor would replace it with a clone, passed the same initial arguments. But that's not the behavior we want here, we want it to just accept that the process has died, and maybe pass an alert to some process for logging or monitoring purposes. That's a built-in option, so it was just an argument in our declaration of the Supervisor in the first place.
The key thing to notice there is that anything that causes that child to crash will resolve the same, safe, way, from unpredicted
packets, to the processor core it's running in being struck by a meteor. The other, perfectly fine child processes will keep chugging along, doing their own thing. Likewise, the Supervisor and Listener are protected from each other's failings. If the Listener fails, the top-level application supervisor will replace it, and likewise for the Supervisor for all the connection handlers.
This synnergy of fault tolerance and concurrency is the key idea of Erlang, and by extension Elixir. It lets you code the pretty path, and accept that there are failure modes you can't or don't want to bother anticipating.
- Aerbil313 3y agoThanks for the detailed response. There’s no other mainstream language which has this fault tolerance built-in, right?
- OkayPhysicist 3y agoDefinitely nothing in the mainstream. The closest you get is a couple of Actor-model libraries for Java and C#, but since languages weren't built from the ground up around it, you end up with a lot of jagged edges that simply don't exist in Erlang or Elixir. Outside of the functional paradigm, there are a lot of things you can express that simply cannot be translated into a distributed environment. The other "in the same ballpark" thing that is much more mainstream is microservice architecture. They aim for the same goal, of achieving concurrency-modeled parallelism and fault tolerance by translating a system into a distributed one, but since microservices tend to be hacked together with a variety of languages with no intrinsic support for the paradigm, you end up with an "ad hoc informally-specified bug-ridden slow implementation of half of Erlang." There's a few projects even further outside the mainstream than Erlang and Elixir that are taking a swing at it, but they're all extremely obscure, and IMO haven't yet demonstrated any novelty worth sacrificing the vast libraries available on the BEAM.