Slide 1

Slide 1 text

Elixir Fundamentals (for Elixir beginners) Brian Zambrano Boulder Elixir Meetup December 6, 2016

Slide 2

Slide 2 text

Me • Originally from SF Bay Area • Writing Python since 2001 • Mostly backend systems + SAAS architecture + (micro)services • Plenty of Django • Plenty of horrible codebases • Looking for a better way

Slide 3

Slide 3 text

What we'll cover • Elixir as a functional language • Data types • Pattern matching • Runtime/processes

Slide 4

Slide 4 text

Why Elixir? • Scalability / concurrency • Distributed system / microservices usually requires tooling, systems and lots of management • Distributed programming built into the language via Erlang/BEAM • Performance • Many languages aren't that good at resource utilization: Python, Ruby, JavaScript/Node • Other more performant languages are a bit too low-level: C, Go, Rust • Functional • Immutable state means concurrency is easier • Immutable state means is harder to write big balls of mud

Slide 5

Slide 5 text

Procedural or OO 1 def calculate_taxes(username, year=2016): 2 try: 3 person = PeopleService(username) 4 except PeopleService.NotFoudException: 5 raise e 6 7 orders = OrdersService.get_orders_for_customer(person) 8 if not orders: 9 return 10 11 tax = calculate_sales_tax(orders, year) 12 13 return prepare_filing(tax)

Slide 6

Slide 6 text

vs. Functional 1 defmodule TaxService do 2 3 def calculate_taxes(username, year // 2016) do 4 {:ok, person} = PeopleService(username) 5 6 person 7 |> OrderService.for_customer 8 |> calculate_taxes(year) 9 |> prepare_filing 10 end 11 12 end

Slide 7

Slide 7 text

$ ls | > grep "\.exs" | > grep -v _test We already think functionally

Slide 8

Slide 8 text

a = [1, 2, 3] func(a) # what is a? Removing mutation

Slide 9

Slide 9 text

a = [1, 2, 3] func(a) a == [1, 2, 3] true # always In Elixir a won't change

Slide 10

Slide 10 text

a = [1, 2, 3] a = func(a) # a has been re-bound # to a new list Rebinding isn't mutation

Slide 11

Slide 11 text

Elixir types 1, 100_000, 0xDEADBEEF, 0b10111 Integer 1.0, 0.31415e1, 31415.0e-5 Float :ok, :is_binary?, :srsly! Atom 1..10 Ranges ~r/[aeiou]/, ~r{[aeiou]} RegEx {:ok, file}, {:error, _} Tuple [1, 2, 3], ['a', 'b'] List %{ name: "bz", state: "CO" } %{ "CO" => "Colorado" } Map true, false, nil Truth <<1 :: size(4), 15 :: size(4)>> # 0001 1111 Binaries

Slide 12

Slide 12 text

Elixir Strings • Single quoted • List of integers • iex represents this as a string if each char is printable • Double quoted • Sequence of UTF-8 bytes • Each byte is a binary

Slide 13

Slide 13 text

Char lists iex(102)> [67, 97, 116] 'Cat' iex(103)> is_list 'hello world' true iex(104)> List.to_tuple 'Cat' {67, 97, 116} iex(107)> 'CaӠ' [67, 97, 19971]

Slide 14

Slide 14 text

Strings iex(128)> "C" == 'C' false iex(135)> [67] 'C' iex(136)> "C" == << 67 :: size(8) >> true iex(138)> String.upcase("cat") "CAT" iex(146)> String.length("CaӠ") 3 iex(147)> byte_size("CaӠ") 5

Slide 15

Slide 15 text

Pattern matching

Slide 16

Slide 16 text

Pattern matching vs variable assignment

Slide 17

Slide 17 text

iex(5)> a = 1 1 iex(6)> 1 = 1 1 iex(7)> 1 = a 1 merely matching up the left and right side of "="

Slide 18

Slide 18 text

1 = a doesn't work in other languages >>> a = 1 >>> 1 = a File "", line 1 SyntaxError: can't assign to literal >>> 1 = 1 File "", line 1 SyntaxError: can't assign to literal >>> irb(main):001:0> a = 1 => 1 irb(main):002:0> 1 = a SyntaxError: (irb):2: syntax error, unexpected '=', expecting end-of-input 1 = a ^ from /usr/local/bin/irb:11:in `' irb(main):003:0> 1 = 1 SyntaxError: (irb):3: syntax error, unexpected '=', expecting end-of-input 1 = 1 ^ from /usr/local/bin/irb:11:in `'

Slide 19

Slide 19 text

1 = a cannot work in Elixir too! a must be defined before matching iex(1)> 1 = a ** (CompileError) iex:1: undefined function a/0 iex(1)> a = 1 1 iex(2)> 1 = a 1

Slide 20

Slide 20 text

iex(8)> list = [1, 2, 3] [1, 2, 3] iex(9)> [a, 2, b] = list [1, 2, 3] iex(10)> a 1 iex(11)> b 3 Matching with lists

Slide 21

Slide 21 text

iex(14)> list = [1, 2, 3] [1, 2, 3] iex(15)> [a, 1, b] = list ** (MatchError) no match of right hand side value: [1, 2, 3] Mis-matching with lists

Slide 22

Slide 22 text

iex(14)> list = [1, 2, 3] [1, 2, 3] iex(15)> [1, _, b] = list [1, 2, 3] Ignoring during matching

Slide 23

Slide 23 text

Pattern matching with Maps

Slide 24

Slide 24 text

iex(2)> me = %{ name: "Brian", age: 43 } %{age: 43, name: "Brian"} iex(3)> %{name: aname} = me %{age: 43, name: "Brian"} iex(6)> aname "Brian"

Slide 25

Slide 25 text

iex(7)> %{name: "Brian"} = me %{age: 43, name: "Brian"} iex(8)> %{name: "Joe"} = me ** (MatchError) no match of right hand side value: %{age: 43, name: "Brian"} Mis-matching with maps

Slide 26

Slide 26 text

Pattern matching in functions

Slide 27

Slide 27 text

Matching based on function arguments 1 defmodule Fib do 2 def fib(1), do: 1 3 def fib(2), do: 1 4 def fib(n), do: fib(n-1) + fib(n-2) 5 end

Slide 28

Slide 28 text

Matching based on function arity 1 defmodule Calculator do 2 3 def inc(n) do 4 n + 1 5 end 6 7 def inc(n, m) do 8 n + m 9 end 10 11 end

Slide 29

Slide 29 text

Matching based on function arity iex(1)> Calculator.inc(5) 6 iex(2)> Calculator.inc(5, 5) 10

Slide 30

Slide 30 text

Processes and Supervisors

Slide 31

Slide 31 text

Simple GenServer defmodule Calculator.Server do use GenServer # API def start_link do GenServer.start_link(__MODULE__, nil, name: :calculator) end def divide(n, m) do GenServer.call(:calculator, {:divide, n, m}) end # end API

Slide 32

Slide 32 text

Simple GenServer # Server callbacks def init(nil) do {:ok, nil} end def handle_call({:multiply, n, m}, _from, _state) do { :reply, n * m, nil } end end

Slide 33

Slide 33 text

Running our Server iex(1)> Calculator.Server.start_link {:ok, #PID<0.119.0>} iex(2)> Calculator.Server.divide(10, 2) 5.0 iex(3)> Calculator.Server.divide(10, 0) ** (EXIT from #PID<0.117.0>) an exception was raised: State: nil nil iex(1)> iex(2)> Calculator.Server.divide(10, 2) ** (exit) exited in: GenServer.call(:calculator, {:divide, 10, 2}, 5000) ** (EXIT) no process (elixir) lib/gen_server.ex:596: GenServer.call/3

Slide 34

Slide 34 text

Running with Supervisor 1 defmodule Calculator.Supervisor do 2 use Supervisor 3 4 def start_link do 5 Supervisor.start_link(__MODULE__, []) 6 end 7 8 def init(_) do 9 children = [ 10 worker(Calculator.Server, []) 11 ] 12 supervise(children, strategy: :one_for_one) 13 end 14 end

Slide 35

Slide 35 text

Running with Supervisor iex(1)> Calculator.Supervisor.start_link {:ok, #PID<0.108.0>} iex(2)> Calculator.divide(10, 2) 5.0 iex(3)> Calculator.divide(10, 0) ** (RuntimeError) Cannot divide by zero (calculator) lib/calculator.ex:6: Calculator.divide/2 iex(3)> Calculator.divide(10, 2) 5.0

Slide 36

Slide 36 text

Resources Introduction to Elixir & Phoenix https://youtu.be/cKeJJldQRgI GenServer/Supervisor gists https://git.io/calculator https://git.io/supervisor Programming Elixir https://pragprog.com/book/elixir/programming-elixir Docs https://elixirschool.com http://elixir-lang.org/getting-started/introduction.html