1. Lab Zero Innovations Inc.
77 Battery Street
San Francisco, CA 94111
415.839.6861
info@labzero.com
labzero.com
Introduction to Elixir
Brien Wankel
Sasha Voynow
2.
3. What is Elixir?
Elixir is a dynamic, functional language designed for building scalable and
maintainable applications.
Elixir leverages the Erlang VM, known for running low-latency, distributed
and fault-tolerant systems, while also being successfully used in web
development and the embedded software domain.
● First release: 2012
● 1.0 in September 2014
● Takes advantage of the Erlang VM
○ Compiled to Erlang (BEAM) bytecode
○ Runs on the Erlang (BEAM) VM
4. Wait, did you say Erlang?
● Created in 1986 by Ericsson for telco exchange software
● Open Source since 1988
● A VM (BEAM)
● A set of libraries and tools (OTP)
● Engineered for
○ Fault tolerance (uptime!)
○ Responsiveness (low latency!)
○ Distributed computing (clustering! concurrency!)
6. OTP
● a set of tools and libraries for implementing fault tolerant server
applications
● Some OTP jargon
○ Process: An extremely lightweight unit of execution
○ Application: A tree/graph of processes that are managed
together.
○ Project: What is normally thought of as an application. A
number of applications started together in the same VM
9. If Erlang is so great, why Elixir?
Erlang has some UX issues
○ Funky, unfamiliar syntax
○ Lots of boilerplate
% module_name.erl
-module(module_name). % you may use some other name
-compile(export_all).
hello() ->
io:format("~s~n", ["Hello world!"]).
# module_name.ex
defmodule ModuleName do
def hello do
IO.puts "Hello World"
end
end
15. The Match Operator (=)
iex(1)> 3 = 3
3
iex(2)> x = 3
3
iex(3)> 3 = x
3
iex(4)> 2 = x
** (MatchError) no match of right hand side value: 3
16. The Pin Operator (^)
iex(1)> x = 3
3
iex(2)> y = x
3
iex(3)> x = 10
10
iex(4)> y
3
iex(5)> ^x = 3
** (MatchError) no match of right hand side value: 3
iex(5)> ^x = 10
10
17. Matching Lists
iex(1)> [a,b,c] = [1,2,3]
[1, 2, 3]
iex(2)> a
1
iex(3)> b
2
iex(4)> c
3
iex(1)> [h | t] = [1,2,3]
[1, 2, 3]
iex(2)> h
1
iex(3)> t
[2, 3]
18. Matching Maps
iex(1)> user = %{name: "Brien", status: :expired, state: "AZ"}
%{name: "Brien", state: "AZ", status: :expired}
iex(2)> %{name: theName} = user
%{name: "Brien", state: "AZ", status: :expired}
iex(3)> theName
"Brien"
iex(4)> %{name: theName, state: "CA"} = user
** (MatchError) no match of right hand side value: %{name:
"Brien", state: "AZ", status: :expired}
19. Matching in case statements
def registered_users do
case load_users_from_database() do
{:ok, users} -> users
{:err, error}
-> IO.puts("something went wrong: #{error}")
[]
end
end
def charge_user(user) do
case user do
%User{account_type: :premium} -> charge_card(user)
_ -> nil
end
end
20. Functions
# different functions
# &Lunchdown.string_to_int/1
def string_to_int(x) do
# ...
end
# &Lunchdown.string_to_int/2
def string_to_int(x, radix) do
# ...
end
# multiple heads of the same function
# &Lunchdown.find/1
def find([_h | _t] = ids) do
# ...
end
def find(id) do
# ...
end
21. Matching in Function Definitions
def handle_cast(:increment, state) do
{:noreply, state + 1}
end
def handle_cast(:decrement, state) do
{:noreply, state - 1}
end
def charge_credit_card(user = %User{account_type: :premium}) do
# charge the credit card
end
def charge_credit_card(_) do
# no-op
end
22. Matching in Function Definitions
# recursion
def sum([]), do: 0
def sum*([h | t]) do
h + sum(t)
end
23. Guard clauses
* You can only use a subset of built-in functions and operators
def charge_credit_card(cc_number, amount) when amount < 0, do: end
def charge_credit_card(cc_number, amount) do
# do some stuff
end
24. Pipelines and the |> operator
● A common elixir idiom for composing functions
● Building up complex transformations of data out of simpler ones in a way
that is readable and intention revealing
● APIs are commonly designed to work well with pipelines
25. Pipelines
def user_by_uid(uid) do
Authorization
|> where([a], a.uid == ^uid)
|> join(:inner, [a], u in User, u.id == a.user_id)
|> preload(:user)
|> Repo.one
end
def word_count(filename) do
increment = fn x -> x + 1 end
count_fun = fn w, m -> Map.update(m, w, 1, increment) end
filename
|> File.stream!
|> Stream.map(&String.trim/1)
|> Stream.flat_map(&String.split/1)
|> Stream.map(&String.downcase/1)
|> Enum.reduce(%{}, count_fun)
end
26. Modules
● The unit of code organization in Elixir
● Just a group of related functions.
defmodule Stack do
defstruct [:items]
# this defines a struct %Stack with the field items
def init do
%Stack{items: []}
end
def push(%Stack(items: items}, item), do: %Stack{items: items ++ [item]}
def pop(%Stack{items: [h | t]}), do: {:ok, h, %Stack{items: t}}
def pop(%Stack{items: []}), do: {:err, "no items"}
end