Learn elixir with me!
Tags: elixir, functional, beginners, javascript
Published: 2021-03-29 12:46:28

Over the past couple years, I've spent most of my development time writing javascript. While javascript is a versatile language, I was itching to learn something new. That's when elixir caught my attention.

## Standing on the shoulders of giants

Elixir is a modern language built on top of the erlang virtual machine (beam). While elixir is a pretty modern language, erlang has been around since 1986.

I first heard about erlang when I took a theory of programming languages course in college. My professor told us about how erlang had made it possible for telecom companies to make extremely scalable and fault-tolerant systems.

[This 2015 article from Wired](https://www.wired.com/2015/09/whatsapp-serves-900-million-users-50-engineers/) discusses how WhatsApp was able to leverage the power of erlang to support 900 million users with only 50 engineers.

Elixir pairs erlang's battle-tested feats of engineering with clean syntax and a modern tool set.

## Getting functional

Elixir is a functional language, so it works a bit differently than the programming languages I've used before.

When I first started writing in elixir, there were three things that stood out to me: returns, scope and piping.

*Want to follow along with the examples? You can install elixir by following the instructions [here](https://elixir-lang.org/install.html) or use an online elixir interpreter [here](https://repl.it/languages/elixir).*

### Returns

Every block in elixir has a return value. But if you skim over some code, you won't notice any return statements.

That's because the return value of any block is implied by the value returned by the last statement in that block.

To make a function return the string `"Hello, world!"`, all you need to do is declare the string:

```elixir
def hello_world() do
  "Hello, world!"
end
```

While the above example is trivial, things get more interesting when you start returning values from blocks that may not return values in other languages.

```elixir
def assign_from_if() do
  a = if 1 > 0 do
    10
  end
  a + 1
end
```

In this example, we have an `if` block that always executes (because `1` is always greater than `0`). The `if` block then returns a value of `10`, which is assigned to the variable `a`. Then, the function returns `a + 1`, which is equal to `11`.

Returning a value from every block may just seem like an interesting quirk, but its value starts to make me sense when we dive into...

### Scope

While I'm still learning about the intricacies of scope in elixir, one thing becomes apparent after playing around with the language:

*A block has access to the values defined in outer scopes, but it cannot affect the bindings in those scopes.*

But what does that actually mean? Here's an example in javascript:

```javascript
let a = 10;

function increment() {
    a++;
}

function print() {
    console.log(a);
}

print(); // 10
increment();
print(); // 11
```

Functions in javascript are able to change the values of variables they have access to. The variable `a` is not defined in the function `increment`, but the function is able to reassign the variable anyways.

For contrast, here's an example in elixir:

```elixir
# Note: the function IO.puts logs a value to the console
def scope_demo() do
  a = 1
  if true do
    IO.puts(a) # 1
    a = a + 1
    IO.puts(a) # 2
  end
  IO.puts(a) # 1
end
```

The `if` block has access to the variable `a`, which is declared outside of the `if` block. However, it doesn't have the ability to re-assign that variable. Instead, on the line `a = a + 1`, the variable `a` is [shadowed](https://en.wikipedia.org/wiki/Variable_shadowing). 

But why does the scope work this way? Why can't we re-assign a variable from within a nested block?

The main answer for this is to limit side effects. Having few to no side effects is a major component of functional programming. In this way, you can have more blocks and functions that are "pure", meaning that they will produce the same output if given the same input.

When you have a lot of pure functions with minimal side effects, this lends well to...

### Piping

If you've ever used bash, this is a concept you'll likely be familiar with. Piping is a feature that allows you to use the output of one statement as the input to the next.

For example, to count the number of items in a directory you could pipe the results of `ls` (which lists the items in a directory) into `wc -w` (which counts the number of words in the given input).

```bash
ls | wc -w
```

Pipes in elixir work in a similar fashion. The value that is piped into a function is used as the first argument in the function call.

Let's break down an example:

```elixir
def ends_with_z?(str) do
  str
  |> String.last()
  |> String.downcase()
  |> Kernel.==("z")
end
```

It's okay if this example doesn't immediately make sense (I've intentionally used some syntax that's specific to elixir).

Here's some things you need to know in order to understand this code:

- The `?` at the end of the function name `ends_with_z?` signifies that the function returns a boolean value
- This pipe operator is represented by `|>`. Whatever value is returned by the previous statement or block is inserted as the first argument in the following function.
- `String.last/1` returns a string containing only the last character of the input string (the `/1` syntax means that `String.last` is a function that accepts one argument).
- `String.downcase/1` returns a lowercase version of the input string.
- `Kernel.==/2` is the equality operator `==`, but in [prefix notation](https://simple.wikipedia.org/wiki/Prefix_notation) as opposed to the typical [infix notation](https://en.wikipedia.org/wiki/Infix_notation). The expressions `a == b` and `Kernel.==(a, b)` are the same. The reason I used the prefix notation in this example was so that I could pipe the result of `String.downcase/1` into it.

*I'm going to publish a more detailed post on elixir pipes in the near future, so [subscribe to my newsletter](https://mailchi.mp/89dc077154c7/brewinstallbuzzwords) or [follow me on DEV](https://dev.to/brewinstallbuzzwords) so you don't miss it.*

## How I've been learning

I've been learning elixir with a combination of reading and working through exercises. The following resources have been the most helpful for me:

### General documentation and help

- [Elixir documentation](https://hexdocs.pm/elixir)
- [Elixir School](https://elixirschool.com/en/)
- [Elixir Forum](https://elixirforum.com/)

### Practice and deeper learning

- [Elixir Succinctly](https://www.syncfusion.com/succinctly-free-ebooks/elixir-succinctly/introduction)
- [Exercism](https://exercism.io/tracks/elixir)
- [Phoenix in Action](https://www.manning.com/books/phoenix-in-action)
- [Read the Docs post about scope](https://elixir-lang.readthedocs.io/en/latest/technical/scoping.html)

## More content

If you liked this, consider [subscribing to my newsletter](https://mailchi.mp/89dc077154c7/brewinstallbuzzwords) or reading one of my other posts:

* [Changing Emoji Skin Tones Programmatically](https://dev.to/brewinstallbuzzwords/changing-emoji-skin-tones-programmatically-4667)
* [I made my first svg animation!](https://dev.to/brewinstallbuzzwords/i-made-my-first-svg-animation-29j3)
* [5 tips for publishing your first npm package](https://dev.to/brewinstallbuzzwords/5-tips-for-publishing-your-first-npm-package-3e4a)
* [4 Hugo Beginner Mistakes](https://dev.to/brewinstallbuzzwords/4-hugo-beginner-mistakes-90h)
* [Introducing meme-type: A translator for internet speak](https://dev.to/brewinstallbuzzwords/introducing-meme-type-a-translator-for-internet-speak-i6a)