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

What Can We (Rubyists) Learn From Elixir

Bce999ccd03c7e20f952995aa57bc4cc?s=47 qhwa
September 23, 2016

What Can We (Rubyists) Learn From Elixir

A brief introduction to Elixir for Rubyists.

Bce999ccd03c7e20f952995aa57bc4cc?s=128

qhwa

September 23, 2016
Tweet

Transcript

  1. What Can We (Rubyists) Learn From Elixir

  2. About Me @qhwa on creator of worked at Alibaba now

    at Helijia.com
  3. About Me work in web industry since 2004 built web

    products with Ruby since 2011 use Elixir since 2016
  4. I ❤ Building Web Products

  5. I ❤ Ruby program happily high productivity easy to transform

    minds to codes high maintainability easy to write expressive and clean codes amazing community
  6. But Ruby does not fit in all situations.

  7. None
  8. Erlang

  9. 1980s for telecom companies still a general-purpose language

  10. for scalable, reliable systems​ constantly provide service with little or

    no downtime
  11. Morden web apps are a good fit, too

  12. Erlang's weakness unfriendly syntax not so shining tools (compared to

    ruby)
  13. Hello.Elixir

  14. Elixir created in 2012 by José Valim was a member

    of Rails core team creator of Devise
  15. Based on Erlang compiled into Erlang VM codes runs in

    Erlang VM (BEAM) can use Erlang libararies
  16. Ships with great tools inspired by Ruby mix (rake) hex

    (gem & bundler) iex (irb) documentating
  17. Ruby-like Syntax IO.puts "Hello, world!"

  18. Tuple {:this, :is, 1, "tuple"}

  19. List list = [:this, "is", 1, "list"] list ++ ["!"]

    # => [:this, "is", 1, "list", "!"]
  20. Keyword List args = [{:timeout, 5000}, {:retry, false}] # equals

    to args = [timeout: 5000, retry: false] # shortcut # can be used as: http(url, timeout: 5000, retry: false)
  21. Map cities = %{cd: "౮᮷", hz: "๺૞"} Map.get(cities, :cd) #=>

    "౮᮷" cities.cd #=> "౮᮷"
  22. Function double = fn (n) -> n * 2 end

    double.(11) #=> 22
  23. Function triple = &(&1 * 3) triple.(11) #=> 33

  24. Module defmodule Greet do def welcome do "Welcome to RubyConfChina!"

    end end Greet.welcome # or Greet.welcome() #=> "Welcome to RubyConfChina!"
  25. “ Alright, but Ruby already offers all of these...

  26. Pattern Matching iex> a = 1 1 iex> a 1

    iex> 1 = a 1 iex> 2 = a ** (MatchError) no match of right hand side value: 1
  27. Pattern Matching iex> 1 = 1 1 iex> 2 =

    1 ** (MatchError) no match of right hand side value: 1
  28. Pattern Matching iex> {x, y} = {100, 50} {100, 50}

    iex> x 100 iex> y 50
  29. Pattern Matching iex> [1, 2, funny | tail] = [1,

    2, 3, 4, 5] [1, 2, 3, 4, 5] iex> funny 3 iex> tail [4, 5]
  30. Pattern Matching iex> {:ok, f} = File.read("./mix.exs") # if read

    successfully {:ok, "..."} iex> {:ok, f} = File.read("NON_EXIST_FILE") # oops ** (MatchError) no match of right hand side value: {:error, :enoent}
  31. Pattern Matching result = do_something() case result do {:ok, result}

    -> process(result) {:error, reason} -> show_msg(reason) end
  32. Pattern Matching defmodule Fibonacci do def fib(0), do: 0 def

    fib(1), do: 1 def fib(n) do fib(n - 2) + fib(n - 1) end end def fib(n) when n > 1 do fib(n - 2) + fib(n - 1) end
  33. Pattern Matching Suppose we are going to implement: [1, 2,

    4, 5, "hello", "world"] [2, 1, 5, 4, "world", "hello"]
  34. defmodule MyList do def swap(list) do _swap(list, []) end defp

    _swap([], current) do current end defp _swap([a], current) do current ++ [a] end defp _swap([a, b | tail], current) do _swap(tail, current ++ [b, a]) end end
  35. Pattern Matching defmodule UsersController do def register(conn, %{"agreed" => false})

    do msg = "Please agree to continue." render(conn, "new.html", alert: msg) end def register(conn, params) do create_user_in_db(params) redirect_to_profile(conn) end end Logic branch Logic trunk
  36. “ your code = small isolated logic branches.

  37. Pipe people = DB.find_customers orders = Orders.for_customers(people) tax = sales_tax(orders,

    2013) filing = prepare_filing(tax)
  38. Pipe filing = prepare_filing( sales_tax( Orders.for_customers( DB.find_customers ), 2013 )

    )
  39. Pipe DB.find_customers |> Orders.for_customers |> sales_tax(2013) |> prepare_filing

  40. Pipe (1..10) |> Enum.map(&(&1 * &1)) |> Enum.filter(&(&1 < 40))

    #=> [1, 4, 9, 16, 25, 36]
  41. Meta Programming

  42. Meta Programming assert 1 == 2 1) test parse messages

    from ... test/my_test.exs:5 Assertion with == failed code: 1 == 2 lhs: 1 rhs: 2
  43. Functional Programming immutable data pure functions

  44. FP: same argument = same result

  45. class Employee def initialize(name, salary) @name = name @salary =

    salary end def change_salary_by(amt) @salary = @salary + amt end def description "#{@name} makes #{@salary}" end end bob = Employee.new("Bob", 10_000) bob.change_salary(1000) puts bob.description # "Bob makes 11000" def change_salary_by(amt) # make function pure by returning a new object self.class.new(@name, @salary + amt) end bob = Employee.new("Bob", 10_000) bob = bob.change_salary_by(1000) FP way
  46. Functional way with Elixir defmodule Employee do defstruct name: nil,

    salary: 0 def change_salary_by(person, amt) do Map.update(person, :salary, amt, &(&1 + amt)) end def description(person) do "#{person.name} makes #{person.salary}" end end %Employee{name: "Bob", salary: 10_000} |> Employee.description() |> Employee.change_salary_by(1000) |> IO.puts # "Bob makes 11000"
  47. “ Reuse your code without worrying.

  48. Process Process 1.spawn 2. wait (block) 2. send message 3.

    receive messages Basic Concurrency in Elixir
  49. Concurrency defmodule SpawnExample do def test do spawn(Greet, :hello, [self,

    "Billy"]) receive do {:ok, msg} -> IO.puts msg end end end
  50. Concurrency defmodule Greet do def hello(from, who) do send(from, {:ok,

    "Hello, #{who}!"}) end end
  51. Concurrency iex> SpawnExample.test Hello, Billy! :ok

  52. Process Process 1.spawn 2. wait (block) 2. send message 3.

    receive messages Basic Concurrency in Elixir (Again)
  53. Paralleling [1, 2, 3, 4] |> Enum.map(&(&1 * &1)) #=>

    [1, 4, 9, 16] # parallel mapping! [1, 2, 3, 4] |> Pmap.map(&(&1 * &1)) #=> [1, 4, 9, 16]
  54. Paralleling defmodule Pmap do def map(collection, f) do me =

    self pids = collection |> Enum.map(fn(element) -> spawn_link(fn -> send(me, {self, f.(element)}) end) end) pids |> Enum.map(fn(pid) -> receive do {^pid, result} -> result end end) end end
  55. “ Processes share nothing.

  56. “Processes everywhere, services everywhere.

  57. OTP -- toolset for massive concurrent systems (should be another

    topic)
  58. (Some) Hard things in Ruby no more exist in Elixir.

  59. Elixir is so different with but close to Ruby.

  60. Take it as your next language to learn if not

    yet, ...
  61. ... and you will find the same fun when learning

    Ruby.
  62. Thanks!

  63. Q/A