As any developer heading towards a burn-out, I spent my summer vacation learning a new programming language. I chose Elixir because it is a functional language with actor-like programming style. I thought that at least the latter feature would help me get started.
Actually, the first thing I ended up figuring out was how the Erlang runtime works. I was interested in how the Erlang processes work in relation to the operating system. It turned out that the concurrency model is not based on spawning multiple user level threads but rather on Erlang runtime abstractions which isolate the running code to the Erlang processes which can communicate with each other via message passing. Erlang runtime has a scheduler which can run multiple processes concurrently on the runtime with a limited set of OS user level threads (number of available CPU threads).
This is hardly surprising after reading about high performing applications written in Erlang. Threads can be expensive especially in the case when the execution switches between threads. Context switches can, for example, lead to increased processor cache misses (or even drop the whole cache) which can cause significant performance losses since the cache is probably the most important thing for CPU performance.
The syntax itself is elegant. The only thing I did not really like is the weak dynamic typing. I do understand the dynamic typing though as it can get rid of some clutter from the code. To prevent unexpected type errors Elixir provides guards (function argument type checks) and a solid pattern matching structure.
Concurrency is the way to go with Elixir and it is taught in all the official documentation. The standard library provides some abstractions like GenServer for the processes and message passing between them. I like the approach, I know from experience that writing raw send and receive methods is cumbersome and can be very confusing to developers not familiar with the actor model. Also, testing is much easier using, for example, GenServer due to its functional implementation of keeping state. This makes the state so much easier to understand at given points of execution than for example in Akka actors where the state changes are based mostly on imperative style side effects (of course you can do functional state in Akka actors as well).
Actually, the first thing I ended up figuring out was how the Erlang runtime works. I was interested in how the Erlang processes work in relation to the operating system. It turned out that the concurrency model is not based on spawning multiple user level threads but rather on Erlang runtime abstractions which isolate the running code to the Erlang processes which can communicate with each other via message passing. Erlang runtime has a scheduler which can run multiple processes concurrently on the runtime with a limited set of OS user level threads (number of available CPU threads).
This is hardly surprising after reading about high performing applications written in Erlang. Threads can be expensive especially in the case when the execution switches between threads. Context switches can, for example, lead to increased processor cache misses (or even drop the whole cache) which can cause significant performance losses since the cache is probably the most important thing for CPU performance.
The syntax itself is elegant. The only thing I did not really like is the weak dynamic typing. I do understand the dynamic typing though as it can get rid of some clutter from the code. To prevent unexpected type errors Elixir provides guards (function argument type checks) and a solid pattern matching structure.
Concurrency is the way to go with Elixir and it is taught in all the official documentation. The standard library provides some abstractions like GenServer for the processes and message passing between them. I like the approach, I know from experience that writing raw send and receive methods is cumbersome and can be very confusing to developers not familiar with the actor model. Also, testing is much easier using, for example, GenServer due to its functional implementation of keeping state. This makes the state so much easier to understand at given points of execution than for example in Akka actors where the state changes are based mostly on imperative style side effects (of course you can do functional state in Akka actors as well).
Comments
Post a Comment