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

Transcript

  1. 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
  2. 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
  3. What is Elixir? • A dynamic, functional language designed for

    building scalable and maintainable applications • Built on the Erlang VM source: http://elixir-lang.org/
  4. 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
  5. Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang • Syntax

    is an acquired taste (for some people) • Missing some language features
  6. 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/
  7. Elixir for Rubyists: LRUG March 2016 @digitalronin "Missing" Features •

    Polymorphic dispatch • Meta-programming • Tooling
  8. 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)
  9. Elixir for Rubyists: LRUG March 2016 @digitalronin alias alias Mystuff.Thing.OtherThing.Whatever

    # Uses Mystuff.Thing.OtherThing.Whatever.foo/1 Whatever.foo(3)
  10. 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)
  11. 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
  12. Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! defmodule

    Hello do def say do IO.puts "Hello, World!" end end Hello.say
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  20. Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> {:ok,

    result} = MyModule.do_something_amazing {:ok, 13}
  21. 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
  22. 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 ...
  23. 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
  24. 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"
  25. Elixir for Rubyists: LRUG March 2016 @digitalronin Anagrams • Sanitise

    the input • Extract words • Calculate signatures • Output sets of matching words
  26. 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
  27. 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
  28. 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
  29. 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
  30. 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
  31. 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
  32. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) output_sets(

    calculate_signatures( extract_words( sanitise(text) ) ) ) end
  33. Elixir for Rubyists: LRUG March 2016 @digitalronin |> • Evaluate

    the left-hand side • Pass result as first parameter of function on the right-hand side
  34. Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) output_sets(

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

    text |> sanitise |> extract_words |> calculate_signatures |> output_sets end
  36. 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/
  37. 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 ... ...
  38. 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
  39. 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
  40. 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"
  41. Elixir for Rubyists: LRUG March 2016 @digitalronin OTP • libraries

    / design patterns / behaviours for building systems which are; • robust • distributed • scalable
  42. 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
  43. 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
  44. 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)
  45. 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
  46. 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
  47. 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
  48. 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
  49. Elixir for Rubyists: LRUG March 2016 @digitalronin iex • elixir

    REPL • tab completion • documentation
  50. 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
  51. 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
  52. 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
  53. Elixir for Rubyists: LRUG March 2016 @digitalronin ExUnit.DocTest defmodule MyApp

    do @doc """ iex> MyApp.foo 123 """ def foo do 456 end end
  54. 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
  55. Elixir for Rubyists: LRUG March 2016 @digitalronin step-through debugger source:

    http://ezine.juggle.org/2012/12/15/karamazov-shuffle-and-variations/
  56. Elixir for Rubyists: LRUG March 2016 @digitalronin Community • Extremely

    beginner-friendly • Good Slack/IRC channel #elixir-lang • "Like Ruby in the early days"
  57. Elixir for Rubyists: LRUG March 2016 @digitalronin Installing • brew

    install elixir • Docker: msaraiva/elixir-dev • http://elixir-lang.org/install.html
  58. 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
  59. 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
  60. Elixir for Rubyists: LRUG March 2016 @digitalronin Why Elixir? •

    Syntax • Powerful • Concurrency • Tooling • Community