Introduction to Elixir and functional programming
Hi. Today I'm gonna introduce Elixir.
Elixir is a functional programming language. It's functional, dynamic, and garbage-collected.
Before I get too much deep dive into Elixir, I like to talk about functional programming languages in general. So what is a functional programming language? So I see programming languages as a way to write instructions to a computer that it can understand.
So in what we do in a functional programming language we use functions, which we got from math, which are just kind of these things that take an input and give us an output. And in functional programming languages that's our main construct: functions.
We probably want some context with this, so I kind of have an image here comparison of my procedural languages, which kind of just have this code that operates on data. Object-oriented languages like Java, Ruby, we have an encapsulation of data and code so our code operates on the data. And in a pure functional language we just have code and the data passes through the code, so it passes to our functions.
This is a sort of kind of joke slide: in a functional programming language we just have a function, so it's kind of like simpler, but we don't have all these design patterns. It's to say, you know, we deal with functions. We don't have mutable states; we don't have other things.
You might be wondering why functional at all. So think about a thing about our functions: if you have any favorite pure function, it just takes an input and it'll give an output, and given the same input we will always get the same output. They're predictable, and predictable means also they're easy to test.
Immutable data, and immutable data means easier concurrency. If your data doesn't change, then it doesn't really matter what other threads are trying to access them. It makes a simpler program for concurrent programs.
Why choose Elixir and its ecosystem
So why Elixir out of all the programming languages? Elixir is, I mean, the main thing is it's functional, right? It has a Ruby syntax so it's developer friendly. It's garbage-collected, which is not necessarily a plus but I don't like manual memory management; maybe you don't either, so that's a plus. It's dynamic so you don't have to type out things; you have more freedom which I like. There's a pretty nice growing community; a lot of Ruby developers are getting into it so it's good.
It has a nice ecosystem. You have like Mix which is like NPM on steroids. ExUnit is a built-in testing framework we get with Elixir. You have Phoenix which is a great web framework, and you have Erlang libraries and the Erlang VM which I'll talk about right now.
Erlang history and design goals
Erlang was a functional programming language invented by Ericsson, a telecoms company. Erlang was invented in the 1980s by Ericsson and, as you can imagine, as a telecoms company you have phone switches and lots of people needing to be talking at the same time and you don't want to cut them off.
What Ericsson needed was a language that was highly concurrent, distributed, always available, and fault-tolerant. They also wanted hot code swapping, so while the systems are running you could swap code in without interrupting it. It's reported that Erlang has availability of nine nines — 99.9999999 percent — which I think I read something like it's like a very small downtime over 20 years. Some companies that have used Erlang are Amazon, RabbitMQ, CouchDB, T-Mobile, Facebook. Other numbers: Yahoo 15 million users, Facebook 100 million, T-Mobile SMS, and WhatsApp is a famous example: they handled incredible volumes like billions of messages with a small team. It's pretty amazing what Erlang is capable of.
Erlang concurrency model and fault tolerance
Erlang's concurrency model is based on something called the actor model. Actors are processes; everything Erlang is processes. This is kind of misleading because you have operating system processes and Erlang processes which are actually implemented as threads, but for the purposes of Erlang, Erlang processes are kind of like threads managed by the BEAM.
An actor is like a process that has its own mailbox so it can communicate with other actors through the mailbox. It sends messages asynchronously and an actor could spawn other actors. It has a hierarchy.
That's the concurrency model for Erlang. In languages like Java you manage threads yourself, and in JavaScript which is single-threaded you have an event queue. An interesting philosophy for Erlang is 'let it crash.' I kind of see Erlang as seeing processes like bumper cars: who cares, I'll just pick them up and put them back; restart them. In Erlang you have supervisors and workers. Workers actually do the work of computation while supervisors manage the workers. If a worker crashes the supervisor will restart it. Supervisors can have supervisors. That's where the fault-tolerant part comes in: a program that has parts or processes that crash will restart itself, which is pretty cool.
BEAM and how Elixir uses Erlang's VM
Where does Elixir come in all this? Elixir is compiled down to Erlang abstract format, another name for BEAM which is the Erlang virtual machine. So both Elixir and Erlang use the Erlang VM which is why Elixir has this powerful model.
Core Elixir syntax and data types
So now to look at their syntax. In Elixir you have atoms which are kind of like Ruby symbols. You can think of them as string constants; when you need to represent a value with a thing that is that value and they have super fast comparisons. JavaScript ES6 also has symbols.
We have tuples which are like a set of related items. Commonly you'll have like you call File.read and you'll get back a tuple where the first item could represent status which is either :ok or :error, and the second could be the error message or the contents. Tuples are stored contiguously in memory.
Maps are your key-value stores; they're kind of like object literals except you don't have objects—it's just pure data. They're related to another thing called structs. Structs are maps with defined fields and default values. With maps you can add properties to it and stuff.
Keyword lists are a list of two-element tuples where the first element of each tuple is an atom. The unique thing about keyword lists is the keys may not be unique and they're usually used for things like Ecto queries or passing options where you can have multiple where clauses. Pattern matching is a pretty powerful feature of functional programming languages and it's in Elixir. You see equals signs constantly but they're not actually assignment operators. They kind of see the structure like an equation: you have to have both sides with the same pattern or structure and you can use this to pull elements from a list into variables.
Functions, pipes, enums, comprehensions, and recursion
All the actual functions you'll define by def. This is how a function would look like. You have pipe operators which are pretty cool; you can feed the results of functions into another so you can write data through your functions.
You have Enum which is a built-in Elixir module giving you functions like reduce, map, and many others. List comprehension is syntactic sugar for looping through data; you don't actually loop but you can compose operations.
Elixir is tail-call optimized. Tail recursion is when you have a recursive function where the last thing you do is a recursive call. The usual signature for tail recursion is when you have another accumulator parameter feeding data into it. If your compiler is tail-call optimized you can jump to another function and back without allocating additional memory. Elixir has this, so you don't have to create stack frames for recursive calls.
Mix is like your NPM on steroids: mix new creates project skeletons, manages dependencies, and has a project file similar to package.json. Documentation in Elixir is first-class: you can write @doc and generate docs with mix docs. Testing is built in with ExUnit: you define tests with test blocks and asserts/refutes. What you really get with Elixir is a developer-friendly syntax, the power of BEAM, access to Erlang libraries, Mix, a built-in test framework, and first-class documentation.