Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Elixir Fundamentals

Elixir Fundamentals

Elixir Fundamentals for Elixir beginners

Cb383c8d8e93cc662e484c149cb3b96b?s=128

Brian Zambrano

December 07, 2016
Tweet

More Decks by Brian Zambrano

Other Decks in Programming

Transcript

  1. Elixir Fundamentals (for Elixir beginners) Brian Zambrano Boulder Elixir Meetup

    December 6, 2016
  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
  3. What we'll cover • Elixir as a functional language •

    Data types • Pattern matching • Runtime/processes
  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
  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)
  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
  7. $ ls | > grep "\.exs" | > grep -v

    _test We already think functionally
  8. a = [1, 2, 3] func(a) # what is a?

    Removing mutation
  9. a = [1, 2, 3] func(a) a == [1, 2,

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

    has been re-bound # to a new list Rebinding isn't mutation
  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
  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
  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]
  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
  15. Pattern matching

  16. Pattern matching vs variable assignment

  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 "="
  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>'
  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
  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
  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
  22. iex(14)> list = [1, 2, 3] [1, 2, 3] iex(15)>

    [1, _, b] = list [1, 2, 3] Ignoring during matching
  23. Pattern matching with Maps

  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"
  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
  26. Pattern matching in functions

  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
  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
  29. Matching based on function arity iex(1)> Calculator.inc(5) 6 iex(2)> Calculator.inc(5,

    5) 10
  30. Processes and Supervisors

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