brew install elixir
- dynamic/functional lang
- runs on Erlang VM
- Actor model - each actor is a separate process in the VM - allows concurrency
Processes are lightweight, and exchange info via messages. This isolates processes, which allows independent GCs and prevents system-wide pauses.
- Mix is the build tool
- Hex is the package manager
- IEx is the repl
- can invoke any Erlang function with no runtime cost
Basic types and operations
40 + 2
"hello" <> " world" # string
:atom # atom/symbol
[1, 2, 3] # list
{1, 2, 3} # tuple
[1, 2] ++ [3, 4] # [1, 2, 3, 4]
# Use / for float and div for int division
# parenthesis are optional when invoking functions
# Floats are 64 bit double precision numbers
10 / 2 # 5.0
div 10, 2 # 5
rem 11, 3 # 2
- Booleans:
is_boolean(true) # true
is_boolean(false) # false
# true and false are same as atoms :true and :false
is_atom(false) # true
is_boolean(:true) # true
is_boolean(:false) # false
-
Functions are referred by name and arity, eg.
is_boolean/1
. -
Strings are UTF-8 encoded
"hello
world" # "hello\nworld"
IO.puts "hello" # Prints "hello" and returns :ok
- When using binary operations,
use
and
,or
,not
when first argument is boolean. - With
&&
,||
,!
the args can be of any type. (Everything exceptfalse
andnil
eval to true, just like in Ruby)
false or is_atom(:example) # true
true and true # true
1 and true # ArgumentError
false and error("This error will never be raised")
1 || true # 1
2 && 3 # 3
false || 11 # 11
!1 # false
!nil # true
- Comparison operators:
===
is more strict compared to==
.
1 == 1.0 # true
1 === 1.0 # false
- Different data types can be compared. This allows sorting algorithms to not worry about data types.
1 < :atom # true
# sorting order
number < atom < reference < functions < port < pid < tuple < maps < list < bitstring
Pattern matching
=
is the pattern match operator
x = 42
x # 42
42 = x # fine
1 = x # ** (MatchError) no match of right hand side value: 42
2 = foo # ** (RuntimeError) undefined function: foo/0
x = 42
assigns 42 to the variable x.42 = x
is ok because the operator only checks if both sides match1 = x
fails because the right side has the value 42 and left side has the value 1, and so the match fails- Variables can only be assigned if they are on the left side
- When we try matching an unknown variable
on the right side,
Elixir thinks it is a function call to
foo
- You can re-bind variables
{ :ok, status } = { :ok, 400 } } }
status # 400
[a, b, c] = [1, 2, 3]
a # 1
[head | tail ] = [1, 2, 3]
head # 1
tail # [2, 3]
[ 4 | tail ] # [4, 2, 3]
^
(pin operator) lets you access previously bound values- it can be used when you need to match against a variable’s value prior to the match
x = 1
{ x, ^x } = { 2, 1 }
x # 2
Strings, binaries, char lists
string = "hello"
is_binary string # true
# UTF8 strings
s = "hełło"
byte_size s # 7
s |> String.length # 5
# each character is a 'code point' whose
# value can be accessed via ? operator
?ł # 322
?a # 97
# Binaries
<<1, 2, 3>> # this is a binary
<<255>> # max balue for binary
<<256>> # 0 -- truncated
# Char lists
s = "hełło"
is_list s # true
to_char_list s # [104, 101, 322, 322, 111]
- Char lists are mainly used for interfacing with old Erlang libs
Keyword lists
- Keyword lists are similar to hashes/dictionaries in other languages
- They map to arrays of key-value tuples
- have 3 characteristics
- keys must be atoms
- keys are ordered
- duplicate keys can exist
list = [a: 1, b: 2]
list == [{:a, 1}, {:b, 2}] # true -- same thing
list[:a] # 1
list ++ [c: 3] # [a: 1, b: 2, c: 3]
# can have same key repeated
list2 = [a: 0] ++ list
list2[:a] # 0 -- values in front override others
# Keyword list based if macro
if false, do: 1, else: 2
# Pattern matching requires number of elements and order to match
[a: a] = [a: 1]
a # 1
Maps
- Key value store
map = %{ :a => 1, 2 => :b }
map[:a] # 1
map[2] # :b
Map.get map, :a # 1
map.a # 1
# pattern match
%{:a => a} = map
a # 1
# updating a map
%{ map | :a => 2 } # %{ :a => 2, 2 => :b }
Modules and functions
- The
defmodule
macro defines a module def
defines a function- Functions have implicit returns
defmodule Math
def sum(a, b) do
a + b
end
end
Fibonacci example:
defmodule Fibonacci do
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n-1) + fib(n-2)
end
This version, using case, maps to the pattern matching version at the VM level.
def fib(n)
case n do
0 -> 0
1 -> 1
n -> fib(n-1) + fib(n-2)
end
end
Ruby-like version:
def fib(n) do
if n < 2
n
else
fib(n-1) + fib(n-2)
end
end
Length of a list:
def len([]), do: 0
def len([h|t]), do: 1 + len(t)
Map:
def map([], f), do: []
def map([h|t], f), do: [ f.(h) | map(t, f) ]
Run length encoding:
defmodule RLE do
def encode(list), do: _encode(list, [])
defp _encode([], result), do: Enum.reverse(result)
defp _encode([a | a | t], result) do
_encode([ {a,2} | tail ], result)
end
defp _encode( [ {a, n}, a | tail ], result ) do
_encode( [ { a, n+1 } | tail ], result )
end
defp _encode( [a | tail ], result ) do
_encode(tail, [ a | result ])
end
end
Default args:
defmodule Concat do
def join(a, b, sep \\ " ") do
a <> sep <> b
end
end
IO.puts Concat.join("Hello", "world") #=> Hello world
IO.puts Concat.join("Hello", "world", "_") #=> Hello_world
Processes
- Achieves concurrency by making use of actors.
- Actors are processes that can perform a specific task.
- We can send messages to an actor to ask it to do something, and it can respond by sending back a message.
Flow example
From Jose’s Elixirconf 2016 Keynote:
Flow.stream("file_name", :line)
|> Flow.from_enumerable()
|> Flow.flat_map(&String.split/1)
|> Flow.partition()
|> Flow.reduce(%{}, fn word, map ->
Map.update(map, word, 1, &(&1 + 1))
end)
|> Enum.into(%{})
Umbrella apps
Ecto
- An Introduction to Elixir’s Ecto Library
- Meet Ecto, The No-Compromise Database Wrapper For Concurrent Elixir Apps
- Building Many-To-Many Associations with Embedded Schemas in Ecto and Phoenix
Plug
Links
- Elixir lang - website
- Phoenix guides - Guides for the Phoenix web framework
- Elixir Quick Reference
- Jose Valim - How I Start
- Elixir Resources
- Elixir Style Guide
- Learning Elixir and Erlang - more resources
OTP
Videos
- Elixir - A modern approach to programming for the Erlang VM on Vimeo, Jose Valim
- Introduction to Elixir, Dave Thomas
- Think Different, Dave Thomas keynote at Elixir Conf 2014
Books
- Études for Elixir by J. David Eisenberg