Elixir (programming language)

From Bauman National Library
This page was last modified on 22 January 2016, at 15:24.


Elixir is a functional, concurrent, general-purpose programming language that runs on the Erlang virtual machine (BEAM). Elixir builds on top of Erlang to provide distributed, fault-tolerant, soft real-time, non-stop applications but also extends it to support metaprogramming with macros and polymorphism via protocols. José Valim is the creator of the Elixir programming language, an R&D project of Plataformatec. His goals were to enable higher extensibility and productivity in the Erlang VM while keeping compatibility with Erlang's tools and ecosystem.


Features

  • A language that compiles to bytecode for the Erlang Virtual Machine (BEAM)
  • Erlang functions can be called from Elixir without run time impact, due to compilation to Erlang bytecode, and vice versa
  • Everything is an expression
  • Meta programming allowing direct manipulation of abstract syntax tree (AST)
  • Polymorphism via a mechanism called protocols. Like in Clojure, protocols provide a dynamic dispatch mechanism. However, this is not to be confused with multiple dispatch as Elixir protocols dispatch on a single type.
  • Support for documentation via Python-like docstrings in the Markdown formatting language
  • Shared nothing concurrent programming via message passing (Actor model)
  • Emphasis on recursion and higher-order functions instead of side-effect-based looping
  • Lightweight concurrency utilizing Erlang's mechanisms with simplified syntax (e.g. Task)
  • Lazy and async collections with streams
  • Pattern matching
  • Unicode support and UTF-8 strings


Installing

Mac OS X

  • Homebrew
    • Update your homebrew to latest: brew update
    • Run: brew install elixir
  • Macports
    • Run: sudo port install elixir

Unix (and Unix-like)

  • Arch Linux (Community repo)
    • Run: pacman -S elixir
  • openSUSE (and SLES 11 SP3+)
    • Add Erlang devel repo: zypper ar -f obs://devel:languages:erlang/ erlang
    • Run: zypper in elixir
  • Gentoo
    • Run: emerge --ask dev-lang/elixir
  • Fedora 17 and newer
    • Run: yum install elixir
  • Fedora 22 and newer
    • Run: dnf install elixir
  • FreeBSD
    • From ports: cd /usr/ports/lang/elixir && make install clean
    • From pkg: pkg install elixir
  • Ubuntu 12.04 and 14.04 / Debian 7

Windows

  • Web installer
    • Download the installer
    • Click next, next, …, finish
  • Chocolatey
    • cinst elixir

Precompiled package

Elixir provides a precompiled package for every release. First install Erlang and then download and unzip the Precompiled.zip file for the latest release.

Once the release is unpacked, you are ready to run the elixir and iex commands from the bin directory, but we would like to recommend you to add Elixir’s bin path to your PATH environment variable to ease development.

In case you are feeling a bit more adventurous, you can also compile from master:

$ git clone https://github.com/elixir-lang/elixir.git
$ cd elixir
$ make clean test

If the tests pass, you are ready to go. Otherwise, feel free to open an issue in the issues tracker on Github.

Installing Erlang

The only prerequisite for Elixir is Erlang, version 18.0 or later, which can be easily installed with Precompiled packages. In case you want to install it directly from source, it can be found on the Erlang website or by following the excellent tutorial available in the Riak documentation.

For Windows developers, we recommend the precompiled packages. Those on a Unix platform can probably get Erlang installed via one of the many package distribution tools.

After Erlang is installed, you should be able to open up the command line (or command prompt) and check the Erlang version by typing erl. You will see some information as follows:

 Erlang/OTP 18 (erts-7) [64-bit] [smp:2:2] [async-threads:0] [hipe] [kernel-poll:false]

Notice that depending on how you installed Erlang, Erlang binaries won’t be available in your PATH. Be sure to have Erlang binaries in your PATH, otherwise Elixir won’t work!

Setting PATH environment variable

It is highly recommended to add Elixir’s bin path to your PATH environment variable to ease development.

On Windows, there are instructions for different versions explaining the process.

On Unix systems, you need to find your shell profile file, and then add to the end of this file the following line reflecting the path to your Elixir installation:

export PATH="$PATH:/path/to/elixir/bin"

Basics

Interactive mode

When you install Elixir, you will have three new executables: iex, elixir and elixirc. If you compiled Elixir from source or are using a packaged version, you can find these inside the bin directory.

For now, let’s start by running iex (or iex.bat if you are on Windows) which stands for Interactive Elixir. In interactive mode, we can type any Elixir expression and get its result. Let’s warm up with some basic expressions.

Open up iex and type the following expressions:

Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help)

iex> 40 + 2
42
iex> "hello" <> " world"
"hello world"

It seems we are ready to go! We will use the interactive shell quite a lot in the next chapters to get a bit more familiar with the language constructs and basic types, starting in the next chapter.

Running scripts

After getting familiar with the basics of the language you may want to try writing simple programs. This can be accomplished by putting the following Elixir code into a file:

IO.puts "Hello world from Elixir"

Save it as simple.exs and execute it with elixir:

$ elixir simple.exs
Hello world from Elixir

Syntax and Semantics

Basic types

  • Numbers
3    # integer
0x1F # integer
3.0  # float
  • Atoms
:hello # atom
  • Tuples
{1,2,3} # tuple

We can access a tuple element with the `elem` function:

elem({1, 2, 3}, 0) #=> 1
  • Lists
[1,2,3] # list

We can access the head and tail of a list as follows:

[head | tail] = [1,2,3]
head #=> 1
tail #=> [2,3]
  • Binaries
<<1,2,3>> # binary
  • Strings and char lists
"hello" # string
'hello' # char list
  • Multi-line strings
"""
I'm a multi-line
string.
"""
#=> "I'm a multi-line\nstring.\n"

Operators

  • Some math
1 + 1  #=> 2
10 - 5 #=> 5
5 * 2  #=> 10
10 / 2 #=> 5.0
  • In elixir the operator `/` always returns a float.
  • To do integer division use `div`
div(10, 2) #=> 5
  • To get the division remainder use `rem`
rem(10, 3) #=> 1
  • There are also boolean operators: `or`, `and` and `not`. These operators expect a boolean as their first argument.
true and true #=> true
false or true #=> true
# 1 and true    #=> ** (ArgumentError) argument error
  • Elixir also provides `||`, `&&` and `!` which accept arguments of any type. All values except `false` and `nil` will evaluate to true.
1 || true  #=> 1
false && 1 #=> false
nil && 20  #=> nil
!true #=> false
  • For comparisons Elixir has: `==`, `!=`, `===`, `!==`, `<=`, `>=`, `<` and `>`
1 == 1 #=> true
1 != 1 #=> false
1 < 2  #=> true
  • `===` and `!==` are more strict when comparing integers and floats:
1 == 1.0  #=> true
1 === 1.0 #=> false
  • We can also compare two different data types:
1 < :hello #=> true

The overall sorting order is defined below: number < atom < reference < functions < port < pid < tuple < list < bit string

To quote Joe Armstrong on this: "The actual order is not important, but that a total ordering is well defined is important."

Control flow

  • `if` expression
if false do
  "This will never be seen"
else
  "This will"
end
  • `unless` expression
unless true do
  "This will never be seen"
else
  "This will"
end
  • `case` allows us to compare a value against many patterns:
case {:one, :two} do
  {:four, :five} ->
    "This won't match"
  {:one, x} ->
    "This will match and bind `x` to `:two`"
  _ ->
    "This will match any value"
end
  • It's common to bind the value to `_` if we don't need it. For example, if only the head of a list matters to us:
[head | _] = [1,2,3]
head #=> 1
  • For better readability we can do the following:
[head | _tail] = [:a, :b, :c]
head #=> :a
  • `cond` lets us check for many conditions at the same time. Can be used instead of nesting many `if` expressions.
cond do
  1 + 1 == 3 ->
    "I will never be seen"
  2 * 5 == 12 ->
    "Me neither"
  1 + 2 == 3 ->
    "But I will"
end

It is common to set the last condition equal to `true`, which will always match.

cond do
  1 + 1 == 3 ->
    "I will never be seen"
  2 * 5 == 12 ->
    "Me neither"
  true ->
    "But I will (this is essentially an else)"
end
  • `try/catch` is used to catch values that are thrown, it also supports an `after` clause that is invoked whether or not a value is caught.
try do
  throw(:hello)
catch
  message -> "Got #{message}."
after
  IO.puts("I'm the after clause.")
end
#=> I'm the after clause
# "Got :hello"

Books to read

  • Programming Elixir by Dave Thomas
  • Elixir in Action by Saša Jurić
  • Introducing Elixir Simon St. Laurent, J. David Eisenberg
  • The Little Elixir and OTP Guidebook by Benjamin Tan Wei Hao
  • Elixir Cookbook
  • Paulo A Pereira

Screencasts

See Also

Elixir Homepage
The Official Elixir Documentation
The Unofficial Elixir Documentation
Elixir at Github
Elixir at Twitter

References

The Official Elixir Documentation
Learn X in Y minutes
Wikipedia Page