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

Elixir for Rubyists

Elixir for Rubyists

Intro to Elixir at London Ruby User Group (LRUG), March 14th 2016

https://skillsmatter.com/skillscasts/7633-elixir-for-rubyists

David Salgado

March 14, 2016
Tweet

More Decks by David Salgado

Other Decks in Programming

Transcript

  1. Elixir for Rubyists: LRUG March 2016 @digitalronin Elixir for Rubyists

  2. Elixir for Rubyists: LRUG March 2016 @digitalronin

  3. Elixir for Rubyists: LRUG March 2016 @digitalronin Who? • David

    Salgado @digitalronin • Messaging applications • ~10 years ruby • CTO & Co-founder of Admoda • We're not hiring
  4. Elixir for Rubyists: LRUG March 2016 @digitalronin Who? • David

    Salgado @digitalronin • Messaging applications • ~10 years ruby • CTO & Co-founder of Admoda • We're not hiring
  5. elixir

  6. What is Elixir?

  7. What is Elixir? • A dynamic, functional language designed for

    building scalable and maintainable applications • Built on the Erlang VM source: http://elixir-lang.org/
  8. Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang • Ericsson

    1986 (open-sourced in 1998) • Designed for telephony applications • Robust & fault-tolerant • Distributed • Hot-swappable source: Wikipedia
  9. Elixir for Rubyists: LRUG March 2016 @digitalronin (some) Phone calls

    are important
  10. Elixir for Rubyists: LRUG March 2016 @digitalronin

  11. Elixir for Rubyists: LRUG March 2016 @digitalronin So, let's all

    use...
  12. Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang • Syntax

    is an acquired taste (for some people) • Missing some language features
  13. Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang -module(module). -compile(export_all).

    -behaviour(gen_server). -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %% Public API start() -> gen_server:start({local, ?MODULE}, ?MODULE, [], []). stop(Module) -> gen_server:call(Module, stop). stop() -> stop(?MODULE). state(Module) -> gen_server:call(Module, state). state() -> state(?MODULE). %% Server implementation, a.k.a.: callbacks init([]) -> say("init", []), {ok, []}. handle_call(stop, _From, State) -> say("stopping by ~p, state was ~p.", [_From, State]), {stop, normal, stopped, State}; source: http://pupeno.com/2010/01/03/erlang-gen_server-template/
  14. Elixir for Rubyists: LRUG March 2016 @digitalronin "Missing" Features •

    Polymorphic dispatch • Meta-programming • Tooling
  15. elixir

  16. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! #!/usr/bin/env

    elixir IO.puts "Hello, World!"
  17. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! import

    IO puts "Hello, World!"
  18. Elixir for Rubyists: LRUG March 2016 @digitalronin import import IO

    import Mystuff.Foo, only: [foo: 1, bar: 1, bar: 2] # call imported function bar(1, 2)
  19. Elixir for Rubyists: LRUG March 2016 @digitalronin alias alias Mystuff.Thing.OtherThing.Whatever

    # Uses Mystuff.Thing.OtherThing.Whatever.foo/1 Whatever.foo(3)
  20. Elixir for Rubyists: LRUG March 2016 @digitalronin alias alias Mystuff.Thing.OtherThing.Whatever

    # Uses Mystuff.Thing.OtherThing.Whatever.foo/1 Whatever.foo(3) alias Mystuff.Thing.OtherThing.Whatever, as: Something # Uses Mystuff.Thing.OtherThing.Whatever.foo/1 Something.foo(3)
  21. Elixir for Rubyists: LRUG March 2016 @digitalronin alias alias Mystuff.Foo

    alias Mystuff.Bar alias Mystuff.Baz # Elixir 1.2+ alias Mystuff.{Foo, Bar, Baz} # Also works for import & require
  22. Elixir for Rubyists: LRUG March 2016 @digitalronin • import •

    alias • require • use
  23. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! defmodule

    Hello do def say do IO.puts "Hello, World!" end end Hello.say
  24. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! defmodule

    Hello do @greeting "Hello to Jason Isaacs" def say(greeting \\ @greeting) do greet greeting end defp greet(greeting), do: IO.puts(greeting) end Hello.say
  25. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! defmodule

    Hello do def say do func = greet() func.("Hello to Jason Isaacs") end defp greet do fn(msg) -> chars = String.to_char_list(msg) IO.puts :string.to_upper(chars) end end end Hello.say # HELLO TO JASON ISAACS
  26. Elixir for Rubyists: LRUG March 2016 @digitalronin Default Parameters def

    foo(bar \\ "baz") # vs. def foo(bar = "baz")
  27. Elixir for Rubyists: LRUG March 2016 @digitalronin Default Parameters defmodule

    Foo do # I do not think it means what you think it means def inconceivable(name = "Buttercup") do "As you wish, #{name}" end end
  28. Elixir for Rubyists: LRUG March 2016 @digitalronin Default Parameters defmodule

    Foo do # I do not think it means what you think it means def inconceivable(name = "Buttercup") do "As you wish, #{name}" end end IO.puts Foo.inconceivable("Buttercup") # -> As you wish, Buttercup
  29. Elixir for Rubyists: LRUG March 2016 @digitalronin Default Parameters defmodule

    Foo do # I do not think it means what you think it means def inconceivable(name = "Buttercup") do "As you wish, #{name}" end end IO.puts Foo.inconceivable("Buttercup") # -> As you wish, Buttercup IO.puts Foo.inconceivable("Vizzini") # -> ** (FunctionClauseError) no function clause matching in Foo.inconceivable/1
  30. Pattern Matching

  31. Elixir for Rubyists: LRUG March 2016 @digitalronin =

  32. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> x

    = 1 1
  33. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> x

    = 1 1 iex(2)> x 1
  34. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> x

    = 1 1 iex(2)> x 1 iex(3)> 1 = x 1
  35. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> x

    = 1 1 iex(2)> x 1 iex(3)> 1 = x 1 iex(4)> 2 = x ** (MatchError) no match of right hand side value: 1
  36. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> x

    = 1 1 iex(2)> x 1 iex(3)> 1 = x 1 iex(4)> 2 = x ** (MatchError) no match of right hand side value: 1 iex(1)> 1 = z ** (CompileError) iex:1: undefined function z/0
  37. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> {:ok,

    result} = MyModule.do_something_amazing {:ok, 13}
  38. Elixir for Rubyists: LRUG March 2016 @digitalronin defmodule Say do

    def hello(:english), do: IO.puts "Hello, LRUG" def hello(:german), do: IO.puts "Guten Abend, LRUG" def hello(_), do: IO.puts "whatever" end Say.hello(:english) Say.hello(:german) Say.hello(:french) ------------------------- $ elixir hello.exs Hello, LRUG Guten Abend, LRUG whatever
  39. Elixir for Rubyists: LRUG March 2016 @digitalronin defmodule Ip do

    ... def is_private("10." <> _remainder), do: true def is_private("192.168." <> _remainder), do: true def is_private("172.16." <> _remainder), do: true def is_private(_ip), do: false ...
  40. Elixir for Rubyists: LRUG March 2016 @digitalronin defmodule User do

    defstruct name: "", admin: false ... def something_awesome(user = %User{ admin: true }) do do_something_awesome user end def something_awesome(user = %User{}) do IO.puts "User #{user.name} is insufficiently awesome" end defp do_something_awesome(user) do ... end end
  41. Elixir for Rubyists: LRUG March 2016 @digitalronin |>

  42. Elixir for Rubyists: LRUG March 2016 @digitalronin Anagrams • Given

    some text, display all the words which are anagrams of each other • Calculate word signatures • signature("ruby") -> bruy • signature("bury") -> bruy • "bury" is an anagram of "ruby"
  43. Elixir for Rubyists: LRUG March 2016 @digitalronin Anagrams • Sanitise

    the input • Extract words • Calculate signatures • Output sets of matching words
  44. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  45. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  46. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  47. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  48. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  49. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) sanitised

    = sanitise(text) words = extract_words(sanitised) hash = calculate_signatures(words) output_sets(hash) end
  50. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) output_sets(

    calculate_signatures( extract_words( sanitise(text) ) ) ) end
  51. Elixir for Rubyists: LRUG March 2016 @digitalronin extract_words( sanitise(text) )

  52. Elixir for Rubyists: LRUG March 2016 @digitalronin sanitise(text) |> extract_words

  53. Elixir for Rubyists: LRUG March 2016 @digitalronin |> • Evaluate

    the left-hand side • Pass result as first parameter of function on the right-hand side
  54. Elixir for Rubyists: LRUG March 2016 @digitalronin foo(a)

  55. Elixir for Rubyists: LRUG March 2016 @digitalronin foo(a) == a

    |> foo
  56. Elixir for Rubyists: LRUG March 2016 @digitalronin foo(a) == a

    |> foo foo(a, b)
  57. Elixir for Rubyists: LRUG March 2016 @digitalronin foo(a) == a

    |> foo foo(a, b) == a |> foo(b)
  58. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) output_sets(

    calculate_signatures( extract_words( sanitise(text) ) ) ) end
  59. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) do

    text |> sanitise |> extract_words |> calculate_signatures |> output_sets end
  60. Concurrency

  61. Elixir for Rubyists: LRUG March 2016 @digitalronin Concurrency • spawn

    • send • receive
  62. Elixir for Rubyists: LRUG March 2016 @digitalronin processes "All Elixir

    code runs inside lightweight threads of execution (called processes) that are isolated and exchange information via messages" source: http://elixir-lang.org/
  63. Elixir for Rubyists: LRUG March 2016 @digitalronin BEAM: The Erlang

    VM beam CPU Core scheduler run queue process process process ... CPU Core scheduler run queue process process process ... CPU Core scheduler run queue process process process ... ...
  64. Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang Process PID:

    <0, 123, 456> code mailbox
  65. Elixir for Rubyists: LRUG March 2016 @digitalronin spawn iex(9)> pid

    = spawn(IO, :puts, [ "Hello, LRUG" ]) Hello, LRUG #PID<0.71.0> iex(10)> Process.alive? pid false
  66. Elixir for Rubyists: LRUG March 2016 @digitalronin send/receive # helpful.exs

    defmodule HowDoYou do def loop do receive do :shutdown -> IO.puts "Shutting down." { sender, msg } -> send(sender, { :ok, "You just #{msg}" }) loop end end end
  67. Elixir for Rubyists: LRUG March 2016 @digitalronin send/receive iex(1)> c

    "helpful.exs" iex(2)> pid = spawn(HowDoYou, :loop, []) iex(3)> send(pid, { self, "sign up to SnapChat" }) iex(4)> send(pid, { self, "give a presentation" }) iex(5)> send(pid, :shutdown) Shutting down. iex(6)> flush "You just sign up to SnapChat" "You just give a presentation"
  68. Elixir for Rubyists: LRUG March 2016 @digitalronin Concurrency • spawn

    • send • receive
  69. Elixir for Rubyists: LRUG March 2016 @digitalronin OTP "Open Telephony

    Platform"
  70. Elixir for Rubyists: LRUG March 2016 @digitalronin OTP • libraries

    / design patterns / behaviours for building systems which are; • robust • distributed • scalable
  71. Elixir for Rubyists: LRUG March 2016 @digitalronin GenServer initialise loop

    terminate state call reply cast
  72. Elixir for Rubyists: LRUG March 2016 @digitalronin GenServer defmodule IpLookup.Worker

    do use GenServer def start_link([]) do :gen_server.start_link(__MODULE__, [], []) end def init(state) do {:ok, state} end def handle_call(ip, _from, state) do {:reply, IpLookup.Lookup.lookup(ip), state} end def lookup(pid, ip) do GenServer.call(pid, ip) end end # IpLookup.Worker.lookup(pid, "1.2.3.4") Server callbacks Client API
  73. Elixir for Rubyists: LRUG March 2016 @digitalronin Agents & Tasks

    • I need a process to; • Do some work concurrently and give me the results • Task • Maintain some state • Agent
  74. Elixir for Rubyists: LRUG March 2016 @digitalronin Agents & Tasks

    # Task geo = Task.async(fn -> geolocate(ip) end) ... location = Task.await geo # Agent { :ok, counter } = Agent.start_link(fn -> 0 end) Agent.update(counter, fn(state) -> state + 1 end) Agent.update(counter, fn(state) -> state + 1 end) Agent.get(counter, fn(state) -> state end) # 2 Agent.stop(counter)
  75. Supervision Trees

  76. "Let it crash"

  77. Elixir for Rubyists: LRUG March 2016 @digitalronin Monitored Process iex(1)>

    pid = spawn(fn -> :timer.sleep(1000) end) #PID<0.67.0> iex(2)> Process.monitor(pid) #Reference<0.0.3.96> iex(3)> flush {:DOWN, #Reference<0.0.3.96>, :process, #PID<0.67.0>, :normal} :ok process process monitor send message on exit
  78. Elixir for Rubyists: LRUG March 2016 @digitalronin Linked Processes process

    process start_link send message on exit iex(4)> {:ok, agent} = Agent.start_link(fn -> 0 end) {:ok, #PID<0.63.0>} iex(5)> Process.exit(agent, :shutdown) ** (EXIT from #PID<0.57.0>) shutdown
  79. Elixir for Rubyists: LRUG March 2016 @digitalronin Supervisor defmodule MyApp

    do use Application def start(_type, _args) do import Supervisor.Spec, warn: false children = [ worker(MyApp.Worker, [arg1, arg2, arg3]) ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] Supervisor.start_link(children, opts) end end
  80. Elixir for Rubyists: LRUG March 2016 @digitalronin Supervision Trees iex(1)>

    :observer.start
  81. Elixir for Rubyists: LRUG March 2016 @digitalronin Supervision Trees iex(1)>

    :observer.start
  82. Tooling

  83. documentation • ExDoc • Searchable, HTML docpages

  84. Elixir for Rubyists: LRUG March 2016 @digitalronin mix $ mix

    run foo.exs $ mix deps.get $ mix test # MIX_ENV $ mix new my_app # --sup --umbrella
  85. Elixir for Rubyists: LRUG March 2016 @digitalronin mix new my_app

  86. Elixir for Rubyists: LRUG March 2016 @digitalronin mix new --umbrella

    my_app
  87. Elixir for Rubyists: LRUG March 2016 @digitalronin iex • elixir

    REPL • tab completion • documentation
  88. Elixir for Rubyists: LRUG March 2016 @digitalronin hex • https://hex.pm

    • Package manager (cf. rubygems) • 1,681 packages (vs. 7,301 gems) as at 08/03/16
  89. error messages

  90. Elixir for Rubyists: LRUG March 2016 @digitalronin error messages defmodule

    Foo do def bar(baz) do "foo" |> String.split "foo" end def bar(:some_value), do: IO.puts("bar") end $ elixir error1.exs error1.exs:3: warning: you are piping into a function call without parentheses, which may be ambiguous. Please wrap the function you are piping into in parentheses. For example: foo 1 |> bar 2 |> baz 3 Should be written as: foo(1) |> bar(2) |> baz(3) error1.exs:2: warning: variable baz is unused error1.exs:6: warning: this clause cannot match because a previous clause at line 2 always matches
  91. Elixir for Rubyists: LRUG March 2016 @digitalronin ExUnit • default

    testing tool • async tests • no rspec-style contexts • https://github.com/sproutapp/pavlov • https://github.com/batate/shouldi • documentation tests
  92. Elixir for Rubyists: LRUG March 2016 @digitalronin ExUnit.DocTest defmodule MyApp

    do @doc """ iex> MyApp.foo 123 """ def foo do 456 end end
  93. Elixir for Rubyists: LRUG March 2016 @digitalronin ExUnit.DocTest defmodule MyApp

    do @doc """ iex> MyApp.foo 123 """ def foo do 456 end end $ mix test Compiled lib/my_app.ex 1) test doc at MyApp.foo/0 (1) (MyAppTest) test/my_app_test.exs:3 Doctest failed code: MyApp.foo === 123 lhs: 456 stacktrace: lib/my_app.ex:3: MyApp (module) . Finished in 0.1 seconds (0.1s on load, 0.00s on tests) 2 tests, 1 failure Randomized with seed 765861
  94. Elixir for Rubyists: LRUG March 2016 @digitalronin step-through debugger

  95. Elixir for Rubyists: LRUG March 2016 @digitalronin step-through debugger

  96. Elixir for Rubyists: LRUG March 2016 @digitalronin step-through debugger source:

    http://ezine.juggle.org/2012/12/15/karamazov-shuffle-and-variations/
  97. Elixir for Rubyists: LRUG March 2016 @digitalronin observer

  98. Elixir for Rubyists: LRUG March 2016 @digitalronin Community • Extremely

    beginner-friendly • Good Slack/IRC channel #elixir-lang • "Like Ruby in the early days"
  99. Elixir for Rubyists: LRUG March 2016 @digitalronin Phoenix http://www.phoenixframework.org

  100. Elixir for Rubyists: LRUG March 2016 @digitalronin Getting Started http://elixir-lang.org

  101. Elixir for Rubyists: LRUG March 2016 @digitalronin Installing • brew

    install elixir • Docker: msaraiva/elixir-dev • http://elixir-lang.org/install.html
  102. Elixir for Rubyists: LRUG March 2016 @digitalronin Learning Resources •

    http://elixir-lang.org/learning.html
  103. Books • https://pragprog.com/book/ elixir/programming-elixir

  104. Books • https://www.manning.com/ books/elixir-in-action

  105. Books • https://pragprog.com/book/ phoenix/programming- phoenix

  106. Elixir for Rubyists: LRUG March 2016 @digitalronin Screencasts • http://elixirsips.com

    • https://www.learnelixir.tv • https://excasts.com • Elixir / Phoenix youtube channel • https://www.youtube.com/channel/ UCVjoWz7bfn6QwU6PV01eoqg
  107. Elixir for Rubyists: LRUG March 2016 @digitalronin Other Stuff •

    Elixir Fountain podcast • http://elixirfountain.com • Twitter • @elixirlang • #myElixirStatus • Elixir London meetup group • http://www.meetup.com/Elixir-London
  108. Elixir for Rubyists: LRUG March 2016 @digitalronin Why Elixir? •

    Syntax • Powerful • Concurrency • Tooling • Community
  109. – No-one. Ever “Elixir doesn't scale.”

  110. elixir |> lrug |> questions? ?