Subscribe now

Basic Elixir Syntax and Concepts [02.13.2017]

Today we'll cover a ton of Elixir syntax in less than five minutes, then look at how to write basic scripts. Let's get started.

Project

We'll be working from the Elixir REPL:

iex

Basic Syntax

We'll start by covering basic syntax.

Atoms

First, let me introduce Atoms. Atoms are constants. Their name is their value.

iex(1)> :this_is_a_constant
:this_is_a_constant
iex(2)> SoIsThis
SoIsThis

Booleans

Booleans are true and false. What's interesting is that they are just syntactic sugar for the corresponding atoms:

iex(3)> :true == true
true
iex(4)> :false == false
true

Numbers

In Elixir you have two types of Numbers: integers and floats.

You can add them:

iex(5)> 1 + 2
3
iex(6)> 2.0 + 3
5.0

Notice that the return value of an operation containing a float is also a float.

You can subtract them:

iex(7)> 2 - 1
1
iex(8)> 3 - 2.0
1.0

Multiply:

iex(9)> 2 * 3
6
iex(10)> 3.0 * 2
6.0

Divide:

iex(11)> 4 / 3
1.3333333333333333
iex(12)> 4 / 2
2.0

Note that division always returns a float. If you need an integer, you want to use the div function:

iex(13)> div(4, 2)
2

And if you want the remainder, there's the rem function:

iex(14)> rem(5, 2)
1

Strings

Strings are surrounded by double-quotes. You can join them together with the <> operator:

iex(15)> "Hello " <> "world"
"Hello world"

They also support interpolation:

iex(16)> who = "josh"
"josh"
iex(17)> "Hello #{who}"
"Hello josh"

Elixir also has unbelievably good support for unicode in strings:

iex(31)> String.upcase("José")
"JOSÉ"

Pattern Matching

Let's talk about pattern matching. If I do this, you probably think assignment:

iex(1)> foo = "bar"
"bar"

So if that's assignment, what is this?

iex(2)> "bar" = "bar"
"bar"
iex(3)> "baz" = "bar"
** (MatchError) no match of right hand side value: "bar"

This is pattern matching. Elixir will bind a variable to perform pattern matching, if possible, and so it can be used a lot like assignment. But it's not the same thing. We'll look at that again momentarily.

Tuples

Tuples are a fixed-size collection of terms. You can create a tuple with curly brackets and commas:

iex(18)> {:like, "this"}
{:like, "this"}

Here we've created a 2-tuple.

Tuples are implemented with contiguous areas of memory, so accessing them is fast. You can use the elem function for that:

iex(19)> person = {"Josh", "Adams", 33}
{"Josh", "Adams", 33}
iex(20)> first = elem(person, 0)
"Josh"
iex(21)> last = elem(person, 1)
"Adams"
iex(22)> age = elem(person, 2)
33

You can also replace an element in a tuple, with put_elem. I just turned 34, so we can fix that:

iex(23)> put_elem(person, 2, 34)
{"Josh", "Adams", 34}

This brings us to immutability. I just modified the person variable, right?

iex(24)> person
{"Josh", "Adams", 33}

Not exactly. put_elem, and anything that appears to mutate a value, instead returns a new value. In elixir, there is no mutating values.

Very frequently, tuples are used alongside pattern matching. Oftentimes functions will return one of either:

iex(25)> {:ok, "some value"}
{:ok, "some value"}
iex(26)> {:error, "something went wrong"}
{:error, "something went wrong"}

We'll talk more about that tomorrow, but I wanted to call this out. These 2-tuples are used to allow pattern matching for success and failure trivially.

Collections

There are a few collection types we care about: Lists, Keyword Lists, and Maps.

Lists

Lists are implemented as linked lists. This means that the time it takes to traverse the list is proportional to the length of the list. It also means that you can't tell how long a list is without traversing the whole thing.

You can build a list in place:

iex(25)> [1, 2, 3]
[1, 2, 3]

You can also append to a list:

iex(26)> [1 | [2, 3]]
[1, 2, 3]

A single-element list is the same as an item appended to the empty list:

iex(27)> [1 | []] == [1]
true

If you're just prepending elements to a list, it's very fast. This is because it just consists of a new list where the tail points to the existing list in memory - there is no need to copy any data.

The first item in a list is known as its head, and the rest is known as its tail. You can get these from a list with the hd and tl functions:

iex(28)> hd([1, 2, 3])
1
iex(29)> tl([1, 2, 3])
[2, 3]

Something to be aware of with lists is that they are used to store character lists, or charlists. These are a common 'string'y type used in Erlang, but less frequently used in Elixir. They are produced by surrounding characters in single quotes:

iex(32)> 'abc'
'abc'

One important thing to note about this is that these are represented by lists of integers that correspond to their ASCII codes. Consequently, lists that only contain integers in the range of printable ASCII codes are a bit confusing the first time you see them printed out:

iex(33)> [97, 98, 99]
'abc'
iex(34)> [97, 98, 99] == 'abc'
true
Keyword Lists

When specifying a small set of options, it's common to use Keyword Lists. These are lists of 2-tuples where the first element is an atom.

iex(35)> [{:foo, "bar"}, {:baz, "whee"}]
[foo: "bar", baz: "whee"]
iex(36)> [foo: "bar", baz: "whee"]
[foo: "bar", baz: "whee"]

You can fetch particular elements out with the following syntax:

iex(37)> [foo: "bar", baz: "whee"][:foo]
"bar"
Maps

Except when handling options, you'll typically want to use Maps instead. Maps have better lookup semantics than Keyword Lists, as they aren't implemented as linked lists.

Here's how you can create a map:

iex(38)> %{foo: "bar", baz: "whee"}
%{baz: "whee", foo: "bar"}

They can be accessed with the same syntax as keyword lists, or by using a dot followed by the field name:

iex(39)> %{foo: "bar", baz: "whee"}.baz
"whee"
iex(40)> %{foo: "bar", baz: "whee"}[:foo]
"bar"

Regular Expressions

Elixir has first-class support for regular expressions. You create a Regular Expression with the r sigil. A sigil is just a tilde (~) followed by a single character:

iex(42)> ~r/foo/
~r/foo/
iex(43)> Regex.match?(~r/foo/, "This contains foo")
true
iex(44)> Regex.match?(~r/foo/, "This does not")
false

Anonymous functions

And of course Elixir is a functional language, so it has first-class support for anonymous functions. Let's define one:

iex(45)> add_two = fn x -> x + 2 end
#Function<6.52032458/1 in :erl_eval.expr/5>

To call an anonymous function, you must use a dot (.) between the function and its arguments.

iex(46)> add_two.(3)
5

And that's it for our survey of the basics of Elixir's syntax. We'll cover more as we run across it, but this should get you started pretty well.

Scripting

Now let's look at basic scripting in Elixir.

Making a script

To make a script, create a file that ends in .exs:

vim foo.exs
add_two = fn x ->
  x + 2
end

# Here we'll use `IO.puts`, which just outputs to the console
IO.puts add_two.(3)

Now you can run the script, with:

elixir foo.exs
# => 5

That's just a basic rehashing of what we've already done, though, albeit with a script to hold our code.

Summary

In today's episode, we covered most of the basic syntax of Elixir. Tomorrow, we'll look at the mix tool, creating modules, defining functions in modules, pattern matching, and using the pipe operator. See you soon!

Resources