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

Intro to Elixir

Intro to Elixir

from my talk at pdx-erlang in April 2014. Introducing Elixir from the perspective of someone who works primarily in a scripting language such as Ruby, Javascript or Python.

Matthew Lyon

April 17, 2014
Tweet

More Decks by Matthew Lyon

Other Decks in Programming

Transcript

  1. What can this help me build? 2004, Rails: Web +

    SQL 2006, jQuery: sane frontend 2010, node: concurrent…?
  2. it compiles to erlang bytecode, integrates w/ existing ecosystem !

    though you’ll have to learn a bit of erlang too ! similar to Clojure, Scala
  3. functional programming 
 treats computation as the evaluation of mathematical

    functions and avoids state and mutable data. Functional programming emphasizes functions that produce results that depend only on blah blah expressions blah blah blah declarative blah blah blah no side effects blah blah blah lambda calculus blah blah blah monads blah blah blah category theory source: wikipedia
  4. functions don’t mutate state a = [3,2,1] a.reverse() // modifies

    a a.concat([4,5]) // new array ! a = Enum.reverse(a) a = Enum.concat(a, [4,5])
  5. processes are light elixir --erl "+P 1000000" \ -r chain.exs

    \ -e "Chain.run(400_000)" {5325361, "Result is 400000"} that’s 400k processes 
 spawning sequentially, running simultaneously in 5.3 seconds on a MacBook Air
  6. upon receiving a message, an actor may send a finite

    number of messages to other actors
  7. base types Integers, 
 arbitrary precision Floats,
 IEEE 754 64-bit

    Atoms, aka symbols Tuples, not arrays Lists, not arrays Binaries, not strings Functions Pids, Ports, References
  8. implemented types Strings, 
 using binaries or 
 “character lists”

    Records,
 using tuples Maps, aka dicts / 
 hashes / POJOs, 
 using Lists Streams, lazy-
 loaded enums Regexs, Ranges, Sets
  9. ranges Enum.map(1..5, fn(i)-> i end) ! start, end can be

    any type enumeration requires integers
  10. functions adder = fn(a) -> fn (b) -> a +

    b end end can pass as values
  11. foo = 1 1 = foo 2 = foo **

    (MatchError) no match of right hand side value: 1
  12. foo = 1 1 = foo ^foo = 2 **

    (MatchError) no match of right hand side value: 2
  13. list = [1,4,9,16] [2,a|rest] = list ** (MatchError) no match

    
 of right hand side value: [1,4,6,9]
  14. ! defmodule Color do def hex(:rgb, vals={r,g,b}) do # no

    conversion needed function signatures 
 perform pattern matching
  15. defmodule Factorial do def of(0), do: 1 def of(x), do:

    x * of(x-1) end declare multiple signatures instead of using if or case
  16. defmodule Factorial do def of(0), do: 1 def of(x) when

    is_number(x) and x > 0, do: x * of(x-1) end guard clauses 
 augment pattern matching
  17. defmodule Factorial do def of(x) when is_number(x) and x >

    0, do: of(x, 1) defp of(0,a), do: a defp of(x,a), do: of(x-1, a*x) end tail-call optimization 
 doesn’t add to the stack
  18. ! defmodule Color do def rgb(r\\0, g\\0, b\\0) do default

    values 
 match left-to-right save a ton of boilerplate
  19. home = &(Regex.match?(~{/home},&1)) ! blob = File.read!('/etc/passwd') lines = String.split(blob,

    "\n") matches = Enum.filter(lines, home) initial = Enum.take(matches, 5) users = Enum.map(initial, get_user) without mutation… lots of asserting?
  20. '/etc/passwd' |> File.read! |> String.split("\n") |> Enum.filter(home) |> Enum.take(5) |>

    Enum.map(get_user) pipeline operator result of expression on left is first param for fn on right
  21. functions are composable echo '/etc/passwd' | xargs -n 1 cat

    | grep '/home' | head -n 5 | cut -d: -f 1
  22. ! if x < 3, do: IO.puts "hello", else: IO.puts

    "bye" and that’s how you use a macro
 there’s also an unless macro
  23. result = cond do whole(val,3) and whole(val,5) -> "FizzBuzz" whole(val,

    3) -> "Fizz" whole(val, 5) -> "Buzz" true -> val end you probably just want function calls instead
  24. ! case thing do c = Color[type=:rgb, {r,g,b}] when is_number(r)

    and r < 1 -> pattern matching with guards & destructuring
  25. ! case File.open("chain.exs") do { :ok, file } -> #

    something { :error, reason } -> # uh oh end this code anticipates a problem with file reading
  26. def function_name(arg1, arg2) do receive do :hello -> IO.puts("hello") val

    -> do_something(val) end end receiving messages is straightforward and uses pattern matching
  27. send(pid, {self, val1}) receive do response -> # handle !

    def function_name(arg1, arg2) do receive do {p,v} -> send(p, process(v)) combine these 
 to wait for a response
  28. def function_name(state) do receive do {p,v} -> send(p, process(v)) end

    function_name(new_state) end an actor 
 may designate the behavior 
 for the next message
  29. def dead_waiter do sleep 500; send(pid, "hi") ! spawn_link(GW,:dead_waiter,[self()]) receive

    do msg -> # never gets here after 100 -> exit(99) linking sets up process
 dependencies
  30. hygenic macros won’t clobber each others’ variables or those of

    the functions that use them ! you can turn hygiene off
  31. defmacro if(condition, body) do yes = Keyword.get(body, :do, nil) no

    = Keyword.get(body, :else, nil) quote do case unquote(condition) do false -> unquote(no) _ -> unquote(yes) quote writes to the output tree unquote writes variable content
  32. defmacro truthy(condition, body) do yes = Keyword.get(body, :do, nil) no

    = Keyword.get(body, :else, nil) quote do case unquote(condition) do val when val in [false, nil, 0, "", [], {}] -> unquote(no) _ -> unquote(yes) like javascript’s falsiness? here you go
  33. defmacro dynamic_def(name) do quote bind_quoted: [name: name] do def unquote(name)()

    do unquote(name) ! [:red, :green] |> Enum.each(&Unholy.dynamic_def(&1)) want to dynamically define 
 methods at compile time? 
 use a binding
  34. defimpl Enumerable, for: Bag do def count(collection) do # returns

    number of items in bag def member?(collection, value) # {:ok, boolean} def reduce(collection, acc, fun) # apply fun to the collection enables Enum to use Bag values
  35. defimpl Access, for: Bag def access(container, key) # gets key

    from container ! my_bag[:thing] enables Bag variables 
 to use [] accessors
  36. defmodule My.Server do use GenServer.Behaviour @version 3 def init(initial) ::

    result def handle_call(request, from, state) 
 :: result def handle_cast(request, state) 
 :: result def handle_info(info, state) :: result def terminate(reason, state) def code_change(oldVsn, state, extra)
 :: status OTP server interface definition
  37. defmodule MyModule do @moduledoc """ ## This is my module

    “"" ! iex(1)> h(MyModule) MyModule ## This is my module
  38. @doc """ ignores its arg and returns 1 """ def

    function(_), do: 1 ! iex(2)> h(MyModule.function/1) def function(_) ignores its arg and returns 1
  39. # mix.exs defp deps do [ { :ex_doc, github:'elixir-lang/ex_doc'} ]

    end ! $ mix docs ExDoc makes HTML docs for you
  40. defmodule MathTest do use ExUnit.Case ! test "math is correct"

    do assert( 2 + 2 = 5 ) end end test and assert are macros
  41. $ mix test Compiled lib/test/supervisor.ex Compiled lib/test.ex Generated test.app 1)

    test math is correct (MathTest) ** (ExUnit.ExpectationError) expected: 5 to match pattern (=): 2 + 2 at test/math_test.exs:5 assertion types are implied 
 by pattern matching & syntax tree
  42. def current_state_name(:event_name, details) do # transition logic { :next_state, :new_state_name,

    details } end ! def new_state_name(:event_name, …) the state machine DSL is sexy
  43. erlang.org Programming Erlang by Joe Armstrong Learn You Some Erlang

    For Great Good by Frank Hébert learnyousomeerlang.com our meetup hosts, erlangsolutions.com
  44. thank you! ! Matthew Lyon twitter / github: mattly speakerdeck.com/mattly/intro-to-elixir

    ! typefaces: Source Sans Pro & Source Code Pro and ChunkFive