Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

Elixir for Rubyists: LRUG March 2016 @digitalronin

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

elixir

Slide 6

Slide 6 text

What is Elixir?

Slide 7

Slide 7 text

What is Elixir? • A dynamic, functional language designed for building scalable and maintainable applications • Built on the Erlang VM source: http://elixir-lang.org/

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

Elixir for Rubyists: LRUG March 2016 @digitalronin (some) Phone calls are important

Slide 10

Slide 10 text

Elixir for Rubyists: LRUG March 2016 @digitalronin

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang • Syntax is an acquired taste (for some people) • Missing some language features

Slide 13

Slide 13 text

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/

Slide 14

Slide 14 text

Elixir for Rubyists: LRUG March 2016 @digitalronin "Missing" Features • Polymorphic dispatch • Meta-programming • Tooling

Slide 15

Slide 15 text

elixir

Slide 16

Slide 16 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Hello, World! #!/usr/bin/env elixir IO.puts "Hello, World!"

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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)

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Elixir for Rubyists: LRUG March 2016 @digitalronin • import • alias • require • use

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Pattern Matching

Slide 31

Slide 31 text

Elixir for Rubyists: LRUG March 2016 @digitalronin =

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

Elixir for Rubyists: LRUG March 2016 @digitalronin = iex(1)> {:ok, result} = MyModule.do_something_amazing {:ok, 13}

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

Elixir for Rubyists: LRUG March 2016 @digitalronin |>

Slide 42

Slide 42 text

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"

Slide 43

Slide 43 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Anagrams • Sanitise the input • Extract words • Calculate signatures • Output sets of matching words

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

Elixir for Rubyists: LRUG March 2016 @digitalronin extract_words( sanitise(text) )

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

Elixir for Rubyists: LRUG March 2016 @digitalronin |> • Evaluate the left-hand side • Pass result as first parameter of function on the right-hand side

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Elixir for Rubyists: LRUG March 2016 @digitalronin def output_anagrams(text) do text |> sanitise |> extract_words |> calculate_signatures |> output_sets end

Slide 60

Slide 60 text

Concurrency

Slide 61

Slide 61 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Concurrency • spawn • send • receive

Slide 62

Slide 62 text

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/

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Erlang Process PID: <0, 123, 456> code mailbox

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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"

Slide 68

Slide 68 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Concurrency • spawn • send • receive

Slide 69

Slide 69 text

Elixir for Rubyists: LRUG March 2016 @digitalronin OTP "Open Telephony Platform"

Slide 70

Slide 70 text

Elixir for Rubyists: LRUG March 2016 @digitalronin OTP • libraries / design patterns / behaviours for building systems which are; • robust • distributed • scalable

Slide 71

Slide 71 text

Elixir for Rubyists: LRUG March 2016 @digitalronin GenServer initialise loop terminate state call reply cast

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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)

Slide 75

Slide 75 text

Supervision Trees

Slide 76

Slide 76 text

"Let it crash"

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

Tooling

Slide 83

Slide 83 text

documentation • ExDoc • Searchable, HTML docpages

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

Elixir for Rubyists: LRUG March 2016 @digitalronin mix new my_app

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

Elixir for Rubyists: LRUG March 2016 @digitalronin iex • elixir REPL • tab completion • documentation

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

error messages

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

Elixir for Rubyists: LRUG March 2016 @digitalronin step-through debugger source: http://ezine.juggle.org/2012/12/15/karamazov-shuffle-and-variations/

Slide 97

Slide 97 text

Elixir for Rubyists: LRUG March 2016 @digitalronin observer

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Phoenix http://www.phoenixframework.org

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Learning Resources • http://elixir-lang.org/learning.html

Slide 103

Slide 103 text

Books • https://pragprog.com/book/ elixir/programming-elixir

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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

Slide 108

Slide 108 text

Elixir for Rubyists: LRUG March 2016 @digitalronin Why Elixir? • Syntax • Powerful • Concurrency • Tooling • Community

Slide 109

Slide 109 text

– No-one. Ever “Elixir doesn't scale.”

Slide 110

Slide 110 text

elixir |> lrug |> questions? ?