Elixir Fundamentals

Elixir Fundamentals

Elixir Fundamentals for Elixir beginners

Cb383c8d8e93cc662e484c149cb3b96b?s=128

Brian Zambrano

December 07, 2016
Tweet

Transcript

  1. 2.

    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
  2. 3.

    What we'll cover • Elixir as a functional language •

    Data types • Pattern matching • Runtime/processes
  3. 4.

    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
  4. 5.

    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)
  5. 6.

    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
  6. 7.

    $ ls | > grep "\.exs" | > grep -v

    _test We already think functionally
  7. 8.
  8. 9.

    a = [1, 2, 3] func(a) a == [1, 2,

    3] true # always In Elixir a won't change
  9. 10.

    a = [1, 2, 3] a = func(a) # a

    has been re-bound # to a new list Rebinding isn't mutation
  10. 11.

    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
  11. 12.

    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
  12. 13.

    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]
  13. 14.

    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
  14. 17.

    iex(5)> a = 1 1 iex(6)> 1 = 1 1

    iex(7)> 1 = a 1 merely matching up the left and right side of "="
  15. 18.

    1 = a doesn't work in other languages >>> a

    = 1 >>> 1 = a File "<stdin>", line 1 SyntaxError: can't assign to literal >>> 1 = 1 File "<stdin>", 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 `<main>' 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 `<main>'
  16. 19.

    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
  17. 20.

    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
  18. 21.

    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
  19. 22.

    iex(14)> list = [1, 2, 3] [1, 2, 3] iex(15)>

    [1, _, b] = list [1, 2, 3] Ignoring during matching
  20. 24.

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

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

    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
  22. 27.

    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
  23. 28.

    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
  24. 31.

    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
  25. 32.

    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
  26. 33.

    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: <snip> 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
  27. 34.

    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
  28. 35.

    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
  29. 36.

    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