5 curious facts about Elixir
Tags: elixir, programming, erlang, codenewbie
Published: 2021-10-14 12:27:58

Elixir is a functional programming language that runs on the BEAM virtual machine, itself used to implement Erlang. Elixir extends Erlang and provides interoperability between both languages. Its syntax is easy to understand and its tools help to standardize code so it's easier to move from one project to the next.

My name is Willian Frantz and I've been an Elixir Alchemist since 2017. I actively contribute to the Brazilian programming community by writing blog posts, speaking at events, and [contributing to open-source libraries](https://x-team.com/blog/contribute-to-open-source-projects/).

Despite being used by companies such as Pinterest, Slack, and Discord, Elixir remains a niche programming language. That's why I want to give you 5 curious facts about Elixir. Hopefully, they will encourage you to give the Elixir programming language a look.

## 1. Functional, but Dynamic too
Elixir is a functional language, but it also allows you to rebind a variable. This is only possible because of the pattern matching operator `=`. For example:

```elixir
iex> a = "Something"
iex> a = "Else"
iex> IO.puts(a)
"Else"
```

## 2. There's No Assign Operator
That's right. Elixir has no assign operator. Instead, it uses `=` as its main pattern matching function. Every time you use `=`, you're pattern matching. For example:

```elixir
iex> a = ["Something", "Else"]
iex> IO.puts(a)
"SomethingElse"

iex> ["Something", a] = ["Something", "Else"]
iex> IO.puts(a)
"Else"

iex> %{name: name} = %{name: "Willian"}
iex> IO.puts(name)
"Willian"
```

Pattern matching is an amazing functionality in Elixir. You can use it to write complex business logic and decision-making capabilities into your code. For example, let's use it to create function polymorphism:

```elixir
def hello("") do
  raise "Empty string provided"
end

def hello(name) do
  IO.puts("Hello World, #{name}!")
end
```

In this example, if we call the `hello/1` function (with `/1` indicating the number of arguments it expects) and give it an empty string as its main argument, it will raise an exception with the message "Empty string provided". Otherwise, it will print "Hello World, {name}".

Another way to create function polymorphism is by using bodyguards. Elixir has some default functions called guards that you can use to control structures (like `case/do`) or apply to a function scope. For example:

```elixir
def hello(name) when is_binary(name) do
  IO.puts("Hello World, #{name}!")
end

def hello(var) do
  raise "Expected a String, received: #{var}"
end
```

We added a guard called `is_binary/1` to our function, which checks if the name is actually a string. If it fails to validate the string, it will raise an exception that says "Expected a String, received: {var}".

## 3. Elixir Goes Meta
Here's a mind-blowing fact: 92% of Elixir's source code is written in Elixir. That's why dealing with constructs is so flexible in Elixir. The programming language is meta: it allows you to write code that writes code, so you can, for example, extend your Elixir program with [macros](https://elixir-lang.org/getting-started/meta/macros.html).

Let's use an Elixir pipe operator to solve chainable calls:

```elixir
# without pipe operator
Enum.frequencies(String.split("Repeated Chars on String", "", trim: true))

# result:
%{
  " " => 3,
  "C" => 1,
  "R" => 1,
  "S" => 1,
  "a" => 2,
  "d" => 1,
  "e" => 3,
  "g" => 1,
  "h" => 1,
  "i" => 1,
  "n" => 2,
  "o" => 1,
  "p" => 1,
  "r" => 2,
  "s" => 1,
  "t" => 2
}

# Same code with pipe operator
"Repeated Chars on String"
|> String.split("", trim: true)
|> Enum.frequencies()
```

The pipe operator breaks chainable calls into a sequential, readable list of code. You could read this as:

I have a string "Repeated Chars on String"
I want to split that string by character and trim it
I want the frequencies of the generated list
We can actually pipe into control structures:

```elixir
# without pipe operator
if Enum.sum(1..10) == 55 do
  IO.puts("1..10 sum = 55")
end

# with pipe operator
1..10
|> Enum.sum()
|> Kernel.==(55)
|> if do
  IO.puts("1..10 sum = 55")
end
```

While I wouldn't recommend to use a pipe operator in this case, it's a good demonstration of [how flexible Elixir can be](https://liftm.io/posts/cursed-elixir.html).

## 4. How Elixir Deals with Strings
Imagine we have both a string `"Hey I am a String"` and a charlist `'Hey I am a Charlist'`. We can cast either whenever we need:

```elixir
iex> "to Charlist" |> to_charlist()
'to Charlist'

iex> 'to String' |> to_string()
"to String"
```

We can also interpolate or concatenate [strings](https://elixirschool.com/en/lessons/basics/strings/):

```elixir
# Interpolation
iex> a = "some"
iex> b = "thing"
iex> "String: #{a}#{b}"
"String: something"

# Concatenation
iex> "String: " <> "something"
"String: something"
```

Since Elixir deals with strings as sequences of bytes, we can also use binary operations to create strings:

```elixir
iex> <<"String: ", "some", "thing">>
"String: something"
```

Or combine binaries with pattern matching:

```elixir
iex> <<"String: ", rest::binary>> = "String: something"
iex> rest
"something"
```

## 5. There Are No Loops
Elixir doesn't have loops. Instead, it uses list comprehensions, recursion, and higher-order functions to iterate over a collection of items. Let's go over each in order.

Here's an example of how you can use **list comprehensions** to generate a list of squared numbers:

```elixir
iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
```

Elixir handles **recursion** pretty well too. Because recursive calls are optimized, they use almost the same amount of space as an imperative loop. Take this programmed factorial, for example:

```elixir
defmodule Fact do
  def call(1), do: 1
  def call(a), do: call(a - 1) * a
end

iex> Fact.call(5)
120
```

We can recursively calculate the factorial of a number without having any call-stack issues. This works even with a much bigger number:

```elixir
iex> Fact.call(50)
30414093201713378043612608166064768844377641568960512000000000000
```

Last but not least, we have **higher-order functions**, a set of functions that take another function as input (e.g. map, reduce, filter, etc). Let's say we want to find all the even numbers in a list.

```elixir
iex> Enum.filter(1..20, &Integer.is_even/1)
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
```

In this example, we used:

- `Enum.filter/2` to filter our collection.
- `1..20` to generate a list of numbers from 1 to 20.
- A capture operator `&` into `Integer.is_even/1` as the function that the filter uses.

## In Conclusion
I hope I've captured your interest with these 5 curious facts about Elixir. In my opinion, Elixir has a few unique features that make it stand out from other languages. If you'd like to know more, check out the following Elixir resources:

- [Learn X in Y minutes where X = Elixir](https://learnxinyminutes.com/docs/elixir/)
- [Elixir introduction](https://elixir-lang.org/getting-started/introduction.html)
- [Elixir School](https://elixirschool.com/en/)