An Introduction to the Elixir Programming Language (And Why I Love It)
I wrote an article a few months ago introducing you to
Golang, and why I loved it so much. While I do indeed still love it, lately I’ve been playing around with a new language; Elixir. I bought the book
Programming Elixir and have been following it through. I’ve already come to love Elixir and hope to show you why in this article.
Whilst I worked for Couchbase a couple of years back, I took a great interest in Erlang, and distributed systems. To me, Erlang is confusing. When I saw Elixir, I immediately knew I was onto something good. Elixir is detailed as a
functional and concurrent language that runs atop of the Erlang VM.
I’ve heard a lot of times, people saying Elixir is ‘Ruby for Erlang’ etc. This theory isn’t fully incorrect, but realistically, it’s mainly just the syntax of Elixir that is influenced by Ruby, as it doesn’t share many of the same programming constructs. It is, of course, similar in ways to Erlang, with it being built atop of the Erlang VM, and giving us direct access to Erlang primitives natively.
Anyway, without further hesitation, let’s dive in and get on with some Elixir.
If you’re running on OSX, installation is as easy as using Homebrew:
~ brew update
~ brew install erlang
~ brew install elixir
Once you’ve done this, you should be able to run:
~ elixir -v
> Elixir 1.0.2
Now Elixir is installed properly, we can open up an
Interactive Elixir and play around with some basic common types.
Start up an
Interactive Elixir REPL by entering
> iex
Variables:
iex(1)> name = "rbin"
#=> "rbin"
Atoms:
In Elixir, an Atom is a constant in which it’s name is it’s own value.
iex(2)> :this_is_an_atom
#=> :this_is_an_atom
iex(3)> :my_atom == :atom
#=> false
Tuples:
We use the Curly Brace notation to define Tuples. In Elixir, Tuples are stored immediately in memory, meaning getting the size of a Tuple, or accessing a Tuple element is fast, but updating or adding elements is expensive as it requires copying the whole tuple in memory.
iex(4)> tuple = {:hello, "world"}
#=> {:hello, "world"}
iex(5)> elem(tuple, 1)
#=> "world"
Lists:
In Elixir, lists are stored in memory, as linked lists. We can update lists trivially by prepending elements, but appending elements is more expensive, as we need to traverse the whole list to figure it’s size.
iex(8)> list = [1, 2, :atom]
#=> [1, 2, :atom]
iex(9)> ["string"] ++ list
#=> ["string", 1, 2, :atom]
iex(10)> list ++ [31]
#=> [1, 2, :atom, 31]
There are very useful in-built functions to be used with lists, including getting the Head & Tail of a list.
iex(11)> hd(list)
#=> 1
iex(12)> tl(list)
#=> [2, :atom]
Anonymous Functions:
In Elixir, functions are first-class citizens, meaning we can pass functions as arguments to other functions. Below, we'll define a variable named
add which contains a function, that we will then pass as an argument to the
is_function/1 func.
iex(14)> add = fn a, b -> a + b end
#Function<12.90072148/2 in :erl_eval.expr/5>
iex(15)> add.(13, 31)
#=> 44
iex(16)> is_function(add)
#=> true
Elixir ships with a
super useful tool named
Mix. Mix is a build tool that allows us to very easily generate, organise, compile, and test our projects. It also makes it easy for us to manage dependencies. (Always a touchy subject!) To create a new project with Mix, we simply do the following:
~ mix new myapp --module MyApp
This will create a directory named
myapp with a few files inside. It will also define a module
MyApp inside
lib/myapp.ex.
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/myapp.ex
* creating test
* creating test/test_helper.exs
* creating test/myapp_test.exs
Your mix project was created successfully.
You can use mix to compile it, test it, and more:
cd myapp
mix test
As we can see, we get all the files needed for a basic project structure. I think this is one of the nicer features available that ship with Elixir. Having project management tools is super useful and a big time-saver.
The file
mix.exs is the main file used to configure our project, and manage dependencies etc. We get a
test/folder, in which we can write
very ruby-like tests for our project. We of course get a
lib/folder, which contains the source files to our project. If we run
mix test on our app, we get the following:
$ mix test
Compiled lib/myapp.ex
Generated myapp.app
.
Finished in 0.04 seconds (0.04s on load, 0.00s on tests)
1 tests, 0 failures
Randomized with seed 543313
Much like Erlang, Elixir uses the
‘Actor’ model for concurrency. All of our code runs inside
Processes, and these processes are isolated from one another. We can make fully concurrent programs in Elixir by spawning Processes and
sending and
receiving messages between them.
In Elixir, we start processes by using the
spawn function, which takes another function as an argument.
iex(1)> spawn(fn -> IO.puts 1 + 1 end)
#=> 2
#PID<0.55.0>
As you can see, we spawned a process which outputted 1 + 1, and also returned it’s Process ID. Returning this Process ID is useful, as we can assign it to a variable, and use it to send messages to the process. Before we do that, we need to create a
receive mechanism to attain the messages we send to the process. (We can go ahead and do this in our
Interative Elixir session, line by line.)