TL;DR

Ever use WhatsApp? It’s arguably the most widely used messaging application across the world. Its original team of approximately 50 engineers scaled it to nearly a billion (900 million) users before its Facebook acquisition. Perhaps this may help you answer “how” that happened…

One Hacker News comment:

Basically they’re a silicon valley company who’ve done everything that’s exactly the opposite of standard silicon valley advise – bootstrapped for first few years, complete apathy or even aversion to silicon valley marketing.

Another Hacker News comment:

I interviewed with them recently. They claimed their success was due to picking up Erlang.

The screenshot is from the following talk by Saša Jurić, the author of Elixir in Action, being the first book that I had ever read on the language when I began. If I could get you to take anything away from this post, it’d be the entirety of this video. Watch it! You won’t be disappointed as it delivers like no other talk you’ve seen – “brutally” to the point giving you an idea about what it is like running real systems in production that the BEAM (Erlang VM) provides and that no other platform provides out of the box; not Haskell, not Scala, not Clojure, not OCaml. You’re going to be coming away with a wishlist of things that you wish that your language or platform had.


the actual things that I love (language features)

I’ve played with my fair share of programming languages – MIPS, C, C++, Rust, Golang, Ruby, Python, JavaScript, Java, Kotlin, Scala, Clojure, OCaml, Haskell. I haven’t worked with them all in production or deployed them in systems used by others. I also don’t claim to have deep knowledge of all of them.

I always tell others that Elixir is a mixture (pun intended) of some of people’s favorite things from some of people’s favorite popular programming languages. Below I list the ones that have stood out to me and exclude features from BEAM aka the Erlang virtual machine (VM), because the video in the TL;DR section above fills that in.

1. syntax from Ruby

If you’ve ever worked with Ruby, the syntax will look very familiar to you. I’m not a huge fan of end but there’s a reason for why it exists

defmodule Foo do
  def bar do
    IO.puts("bar")
  end
end

2. docstrings and doctests from Python

One of the biggest issues that professional developers have when adding comments to code is this idea known as “decaying documentation”. It simply implies that there is an oft-forgotten update to the corresponding comment (documentation) when a code change is performed. Python provides a facility called doctests to solve this problem – if a code example is provided in the “comment” a run of the test suite would reveal a test failure should that example no longer work after the code change.

defmodule MyApp.Hello do
  @moduledoc """
  This is the Hello module.
  """
  @moduledoc since: "1.0.0"

  @doc """
  Says hello to the given `name`.

  Returns `:ok`.

  ## Examples

      iex> MyApp.Hello.world(:john)
      :ok

  """
  @doc since: "1.3.0"
  def world(name) do
    IO.puts("hello #{name}")
  end
end

3. comprehensions from Python

List comprehensions anyone? I care a lot about the ergonomics of a language and its ability to express declaratively what I want to do. While Python is a language that a lot of people love, I’ve a lot of gripes with it, primarily that it promotes procedural code, but list comprehensions is certainly one of my favorite and expressive language features despite what I find to be a clunky functools module that seems like an awkward bolt-on. Guido, Python’s creator, himself noted that Python had no intention of providing functional programming facilities – so needless to say, a bit of a second-class citizen.

Squaring a list of numbers:

Python

[n * n for n in range(1, 5)]

Elixir

for n <- [1, 2, 3, 4], do: n * n

4. protocols for polymorphism from Clojure

If you’re familiar with interfaces (more like Golang than Java) in other languages, this is how it is done in Clojure and Elixir. This facility makes it simple for expanding the use of a behavior to new types of data or schemas your application may need to adapt to next (i.e., functions; not to be confused with Erlang/Elixir “behaviours”). But the secondary benefit is that it declares a set of functions anyone contributing to an open-source library written in Elixir can implement without mangling with existing source code (following the open/closed principle from SOLID)

# protocol definition
defprotocol Size do
  @doc "Calculates the size (and not the length!) of a data structure"
  def size(data)
end

# protocol implementations
defimpl Size, for: BitString do
  def size(string), do: byte_size(string)
end

defimpl Size, for: Map do
  def size(map), do: map_size(map)
end

defimpl Size, for: Tuple do
  def size(tuple), do: tuple_size(tuple)
end

5. mix (build tool) inspired by Bundler in Ruby

Tooling in some language ecosystems is nil or a second-class citizen. One thing about the Ruby and Elixir communities are that they treat them as first-class citizens giving you tools that don’t get in your way and are just a pleasure to work with. Those of you that have spent any significant time with JavaScript, Ruby, or Rust may be familiar with the name, Yehuda Katz, who served as a major influence and mentor to José Valim (Elixir’s BDFL/creator) when he was a Google Summer of Code participant/mentee – with both having been Rails core team members. Katz is known for more than Ruby on Rails – he’s the creator of quite a number of well-loved build tools in various ecosystems: Bundler (Ruby), Cargo (Rust), and Yarn (JavaScript). Elixir follows in this spirit by doing the same with Mix.

6. lazy evaluation inspired by Scalaz in Scala and Haskell

If you’re familiar with reducees, iteratees, iterators, generators, or just the idea of lazy evaluation to reduce space complexity or the memory footprint of code that could eagerly load/buffer all data into memory before operating on it, and perhaps doing it all again, and again… could be expensive, then you are right. Not every language has facilities to avoid this

Multiply the range between [1, 100000] by 3, then filter the odds, and sum them up:

Ruby (eager) - declarative, but expensive

This creates an array of 100,000 elements, maps into a new array of 100K elements, then selects into another array of 100K elements, before summing them.

(1..100000).map { |n| n * 3 }.select { |n| n % 2 != 0 }.sum

Python (eager) - imperative and expensive

nums = []
for n in range(1, 100001):
  n = n * 3
  if n % 2 != 0:
    nums.append(n)

Python 3 (lazy) - declarative and inexpensive

[n * 3 for n in range(1, 100001) if n * 3 % 2 != 0]

Elixir (eager versions) - declarative, but expensive

# using comprehensions
for n <- 1..100_000, rem(n * 3, 2) != 0, do: n * 3

# using Enum functions and pipes
import Enum
# the `&` is known as the capture operator,
# capturing the argument by its number in an anonymous function,
# but one may think of the expression as a lambda
1..100_000
|> map(& &1 * 3)
|> filter(& rem(&1, 2) != 0)
|> sum()

Elixir (lazy version) - declarative AND inexpensive

You may think of it as a “pipeline” of operations whereby each element is piped from one function to another at the end (when Enum.sum) is invoked, without the creation of intermediate lists like the above example or the Ruby example.

# Simply replace most references of `Enum` with `Stream`
import Stream

1..100_000
|> map(& &1 * 3)
|> filter(& rem(&1, 2) != 0)
|> Enum.sum()

7. StreamData and property-based testing from Haskell

For the functional programming and purists hailing from Haskell land, you may be familiar with QuickCheck and property-based testing. The basic premise is that most of us write “example-based” unit tests whereby the developer is left up to the task of coming up with and are limited to the examples (i.e., corner/edge cases) they’re familiar with at the time of authoring some bit of software. Property-based testing is about random data generation that aids with expanding test coverage. Moreover, the developer can assert some guarantees about the code and the properties that it satisfies. It won’t cover all possible values that a function can handle, but does help in reducing the number of bugs in deployed software. Haskell’s QuickCheck has inspired similar external libraries in the Python, Ruby, and Rust communities that I’m familiar with.

8. pipe operator from standard ML (SML) or Unix

If you’ve ever done cat foo.txt | grep 'bar' from a shell you’re familiar with what |> does in Elixir. However, in most programming languages that most of us have spent time in, code looks like this:

Where the ordering of operations happen from inside out…

foo(bar(baz(new_function(other_function(data)))))

Elixir’s pipe operator makes the ordering explicit and obvious:

data
|> other_function()
|> new_function()
|> baz()
|> bar()
|> foo()

# - OR -
data |> other_function() |> new_function() |> baz() |> bar() |> foo()

my introduction to Elixir (background)

I was first introduced to Elixir back in 2014 when I had met someone that ran a local Elixir meet-up before anyone knew what the language was. This was all before The Phoenix web framework existed. Working mostly on the web and in JavaScript and some NodeJS at the time, I didn’t take a gander at all – very different from my approach and intrigue with programming languages now.

However, back in 2016, I began to hear murmurs of Elixir at the company that I was employed with. I had a CTO that was always somewhat enamored with the shiny new thing. He and our principal engineer (that had some Erlang experience) test-drove Elixir in production with an inventory service that aided our warehouse ops – this company used real physical storage for storing all its products. It went successfully, and I was asked if I wanted to give it a whirl on the core products team. I remember thinking, “hell yea!” This provided me an opportunity to understand what the friend that I had met in 2014 was raving about, but also an opportunity to play with and deploy a truly (albeit not pure) functional language and platform into production.

For the unfamiliar, the Elixir programming language was created by José Valim, a Rails core team member that ranked as one of the top 3 (#6 at the time of this post) contributors of all time. To say that he was prolific is putting it lightly. After attempts at making inroads with concurrency on Rails with mixed success, he discovered Erlang through a tour of languages, by reading a book titled, Seven Languages in Seven Weeks by Bruce Tate who would eventually author one of the first Elixir books along with Programming Phoenix along with Chris McCord, the primary author of the popular framework built with Elixir.

I was able to contribute to the majority of two API clients in Elixir - one internal and another open-sourced (external) SDK whose platform didn’t have one for such a new language. Both required writing recursive descent parsers, which was really quite fun. My work and production experience with Elixir also heavily involved the use of Phoenix, the web framework, for completely rebuilding one of the core experiences on the e-commerce platform – the product listings page, and most challenging of all, how to model and traverse a product taxonomy with immutable datatypes to render navigation on the site.

I haven’t had a chance to work with Elixir in production in about two years, but have been keeping my ear to the community over that time and have several small projects I work on and run personally outside of building for the web.