Slide 1

Slide 1 text

What Can We (Rubyists) Learn From Elixir

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

About Me work in web industry since 2004 built web products with Ruby since 2011 use Elixir since 2016

Slide 4

Slide 4 text

I ❤ Building Web Products

Slide 5

Slide 5 text

I ❤ Ruby program happily high productivity easy to transform minds to codes high maintainability easy to write expressive and clean codes amazing community

Slide 6

Slide 6 text

But Ruby does not fit in all situations.

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Erlang

Slide 9

Slide 9 text

1980s for telecom companies still a general-purpose language

Slide 10

Slide 10 text

for scalable, reliable systems​ constantly provide service with little or no downtime

Slide 11

Slide 11 text

Morden web apps are a good fit, too

Slide 12

Slide 12 text

Erlang's weakness unfriendly syntax not so shining tools (compared to ruby)

Slide 13

Slide 13 text

Hello.Elixir

Slide 14

Slide 14 text

Elixir created in 2012 by José Valim was a member of Rails core team creator of Devise

Slide 15

Slide 15 text

Based on Erlang compiled into Erlang VM codes runs in Erlang VM (BEAM) can use Erlang libararies

Slide 16

Slide 16 text

Ships with great tools inspired by Ruby mix (rake) hex (gem & bundler) iex (irb) documentating

Slide 17

Slide 17 text

Ruby-like Syntax IO.puts "Hello, world!"

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

List list = [:this, "is", 1, "list"] list ++ ["!"] # => [:this, "is", 1, "list", "!"]

Slide 20

Slide 20 text

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)

Slide 21

Slide 21 text

Map cities = %{cd: "౮᮷", hz: "๺૞"} Map.get(cities, :cd) #=> "౮᮷" cities.cd #=> "౮᮷"

Slide 22

Slide 22 text

Function double = fn (n) -> n * 2 end double.(11) #=> 22

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Module defmodule Greet do def welcome do "Welcome to RubyConfChina!" end end Greet.welcome # or Greet.welcome() #=> "Welcome to RubyConfChina!"

Slide 25

Slide 25 text

“ Alright, but Ruby already offers all of these...

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Pattern Matching iex> {x, y} = {100, 50} {100, 50} iex> x 100 iex> y 50

Slide 29

Slide 29 text

Pattern Matching iex> [1, 2, funny | tail] = [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] iex> funny 3 iex> tail [4, 5]

Slide 30

Slide 30 text

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}

Slide 31

Slide 31 text

Pattern Matching result = do_something() case result do {:ok, result} -> process(result) {:error, reason} -> show_msg(reason) end

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Pattern Matching Suppose we are going to implement: [1, 2, 4, 5, "hello", "world"] [2, 1, 5, 4, "world", "hello"]

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

“ your code = small isolated logic branches.

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

Pipe (1..10) |> Enum.map(&(&1 * &1)) |> Enum.filter(&(&1 < 40)) #=> [1, 4, 9, 16, 25, 36]

Slide 41

Slide 41 text

Meta Programming

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Functional Programming immutable data pure functions

Slide 44

Slide 44 text

FP: same argument = same result

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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"

Slide 47

Slide 47 text

“ Reuse your code without worrying.

Slide 48

Slide 48 text

Process Process 1.spawn 2. wait (block) 2. send message 3. receive messages Basic Concurrency in Elixir

Slide 49

Slide 49 text

Concurrency defmodule SpawnExample do def test do spawn(Greet, :hello, [self, "Billy"]) receive do {:ok, msg} -> IO.puts msg end end end

Slide 50

Slide 50 text

Concurrency defmodule Greet do def hello(from, who) do send(from, {:ok, "Hello, #{who}!"}) end end

Slide 51

Slide 51 text

Concurrency iex> SpawnExample.test Hello, Billy! :ok

Slide 52

Slide 52 text

Process Process 1.spawn 2. wait (block) 2. send message 3. receive messages Basic Concurrency in Elixir (Again)

Slide 53

Slide 53 text

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]

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

“ Processes share nothing.

Slide 56

Slide 56 text

“Processes everywhere, services everywhere.

Slide 57

Slide 57 text

OTP -- toolset for massive concurrent systems (should be another topic)

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Elixir is so different with but close to Ruby.

Slide 60

Slide 60 text

Take it as your next language to learn if not yet, ...

Slide 61

Slide 61 text

... and you will find the same fun when learning Ruby.

Slide 62

Slide 62 text

Thanks!

Slide 63

Slide 63 text

Q/A