class: center, middle # Elixir and Ruby .pull-left[  http://elixir-lang.org ] .pull-right[  http://ruby-lang.org ] Nick Veys / [@nickveys](http://twitter.com/nickveys) / nick@codelever.com [](http://codelever.com) --- # What is Elixir? * Functional language built on the Erlang VM and OTP * Created by José Valim (Devise, Rails) to take advantage of Erlang's existing capabilities, with a nicer syntax and modern set of tools * With Erlang comes a host of powerful stuff (fault tolerance, distributed processing, hot code-reloading etc) * Syntax and conventions heavily influenced by Ruby * If you know Ruby, you know quite a bit of Elixir already .pull-left[ ### Elixir ```elixir iex> IO.puts "Hello, Elixir!" Hello, Elixir! :ok iex> ["One", :two, 3] ["One", :two, 3] ``` ] .pull-right[ ### Ruby ```ruby irb> puts "Hello, Ruby!" Hello, Ruby! => nil irb> ["One", :two, 3] => ["One", :two, 3] ``` ] ??? * Erlang made at Ericsson for telephony switches/exchanges * Highly distributed * High availability * Claiming 9 Nine's in one product (~31.5ms per year of downtime) * Erlang philosophy "Let It Crash", designed to handle failures more declaritively * Can avoid handling errors and exceptions in many cases, code w/less complexity * OTP: Erlang standard library, Open Telecom Platform * Calls into Erlang have no additional overhead, everything is compiled to BEAM bytecode * You should totally check out that powerful stuff! * Hot code reloading * Along w/that migrating in-memory data structures as code reloads --- # Some Similarities * Open Source with friendly communities * High-level, dynamic typing, strong typing * Can be run interpreted for use in scripting * REPL, build tools, package management all part of language distribution * Many parts of the syntax are identical or nearly so # Some Differences * Ruby can be written in many ways, but typically object-oriented * Elixir is functional, data structures are immutable! * In Elixir, state is achieved using Actors * Elixir (and Erlang) have no "traditional" threading, processes instead * Being built on Erlang, Elixir has first-class support for running in distributed environments ??? Similarities * e.g. Unit tests are run scripted, not important to compile ahead of time * MINASWAN -> JINASWAN Differences * Actor model: own memory space, concurrent, send/recv messages from a mailbox * All concurrent processing is handled by the VM, scheduled using its own light-weight processes * Not atypical to have thousands of processes running in a VM * VM doesn't care where a process is, can be local or a remote machine --- # Code! .pull-left[ ### Elixir ```elixir iex> [1, 2.1, 2.1e2, 0x2F, 0b0101, 10_000] [1, 2.1, 210.0, 47, 5, 10000] iex> [true, false, :atom, :"stringy-atom"] [true, false, :atom, :"stringy-atom"] iex> "Iñtërnâtiônàlizætiøn" "Iñtërnâtiônàlizætiøn" iex> String.upcase("Iñtërnâtiônàlizætiøn") "IÑTËRNÂTIÔNÀLIZÆTIØN" iex> String.upcase("weiß") "WEISS" iex> foo = "Interpolation" iex> "String #{foo}" "String Interpolation" irb> 'Single' 'Single' iex> [83, 105, 110, 103, 108, 101] 'Single' ``` ] .pull-right[ ### Ruby ```ruby irb> [1, 2.1, 2.1e2, 0x2F, 0b0101, 10_000] => [1, 2.1, 210.0, 47, 5, 10000] irb> [true, false, :symb, :"stringy-symb"] => [true, false, :symb, :"stringy-symb"] irb> "Iñtërnâtiônàlizætiøn" => "Iñtërnâtiônàlizætiøn" irb> "Iñtërnâtiônàlizætiøn".upcase => "IñTëRNâTIôNàLIZæTIøN" irb> "weiß".upcase => "WEIß" irb> foo = "Interpolation" irb> "String #{foo}" => "String Interpolation" irb> 'Single' => "Single" irb> [83, 105, 110, 103, 108, 101] => [83, 105, 110, 103, 108, 101] ``` ] ??? * Atoms in Erlang very similar to Ruby, only created once * Erlang they are not GC'd * Ruby as of 2.2 they can be * Elixir has better built-in support for Unicode * Ruby needs [unicode-utils](http://unicode-utils.rubyforge.org) * Char lists are not strings! * Usually for Erlang interop --- # Code! .pull-left[ ### Elixir ```elixir iex> {"Tuple", "of", "Strings"} {"Tuple", "of", "Strings"} iex> ["List", "of", "Strings"] ["List", "of", "Strings"] iex> [keyword: "lists", are: "fun"] [keyword: "lists", are: "fun"] iex> [{:just, "lists"}, {:of, "tuples"}] [just: "lists", of: "tuples"] iex> %{:map => "2", "key" => :varies} %{:map => "2", "key" => :varies} ``` ] .pull-right[ ### Ruby ```ruby irb> # No tuples, but a list ...> # is pretty close irb> ["List", "of", "Strings"] => ["List", "of", "Strings"] irb> # No real Ruby equivalent ...> # that I know of irb> # No real Ruby equivalent ...> # that I know of irb> {hash: "with", "!" => :x, 4 => "key"} => {:hash=>"with", "!"=>:x, 4=>"key"} ``` ] ??? * Tuples - not collections, meant for storing multiple things together * Other langs like Python have them * Lists - internally they are linked lists --- # More Code! .pull-left[ ### Elixir ```elixir iex> f = fn x -> x + 1 end iex> f.(2) 3 iex> defmodule Foo do ...> def bar(x), do: x + 2 ...> end iex> Foo.bar(10) 12 iex> # Sorry! iex> # No! iex> # Classes! iex> defmodule Data do ...> defstruct x: nil, y: 2, z: "zee" ...> end iex> d = %Data{x: 25, z: "why"} %Data{x: 25, y: 2, z: "why"} iex> d.x 25 ``` ] .pull-right[ ### Ruby ```ruby irb> f = ->(x) { x + 1 } irb> f.(2) => 3 irb> module Foo ...> def self.bar(x) x + 2 end ...> end irb> Foo.bar(10) => 12 irb> class Baz ...> def quux(x) x - 1 end ...> end irb> Data = Struct.new(:x, :y, :z) irb> d = Data.new(25, 2, "why") => #
irb> d.x => 25 ``` ] ??? * Unnamed functions similar to lambda/proc in Ruby * Last expression of a function is it's returned value * Modules are just bags of functions * Can't use `def` outside of a module, sugar to assign the function to that name w/in module * Structs are just maps w/extra metadata --- # Nifty Code! ### Pipelines ```elixir # without pipelining Enum.sum(Enum.filter(Enum.map(Enum.map(~w(0 1 2 3 4 5)), &(&1 * 10)), &Kernel.<(&1, 30))) # => 30 # with pipelining ~w(0 1 2 3 4 5) |> Enum.map(&String.to_integer/1) |> Enum.map(&(&1 * 10)) |> Enum.filter(&Kernel.<(&1, 30)) |> Enum.sum # => 30 ``` Formalizes something you could achieve in Ruby if writing things functionallyish. ```ruby %w(0 1 2 3 4 5).map(&:to_i) .map { |i| i * 10 } .select { |i| i < 30 } .reduce(0, &:+) # => 30 ``` ??? ## Pipelines * Potential problem with functional languages is the constant passing of data you need to do --- # Nifty Code! ### Pattern Matching ```elixir iex> map = %{key1: 25, key2: [1, 2, 3, 4, 5]} iex> %{key1: val1, key2: [head | tail]} = map iex> val1 25 iex> head 1 iex> tail [2, 3, 4, 5] ``` I can use this to enforce an expected structure. ```elixir iex> func = fn x -> ...> if x < 10 do ...> {:ok, "good job"} ...> else ...> {:error, "what are you thinking"} ...> end ...> end iex> {:ok, result} = func.(5) {:ok, "good job"} iex> result "good job" iex> {:ok, result} = func.(15) ** (MatchError) no match of right hand side value: {:error, "what are you thinking"} ``` ??? * First example: Just decomposition * Available in other languages like Scala, Haskell. ES6 has a subset of it. * `=` is not assignment, it is a *match operator*, it makes both sides of the expression equivalent * You'll find you use this nearly everywhere --- # Nifty Code! ### Pattern Matching ```elixir iex> buzz = fn ...> (0, 0, _) -> "FizzBuzz" ...> (0, _, _) -> "Fizz" ...> (_, 0, _) -> "Buzz" ...> (_, _, x) -> x ...> end iex> fizz = fn x -> IO.puts buzz.(rem(x, 3), rem(x, 5), x) end iex> Enum.each(1..10, fizz) 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz :ok ``` ??? * anonymous functions can have multiple implementations with matching * first one to match wins * for multiples of three print "Fizz" instead of the number * for the multiples of five print "Buzz" * for numbers which are multiples of both three and five print "FizzBuzz" --- # Nifty Code! ### Pattern Match All The Things! Choose what function gets invoked based on what was passed to it. ```elixir defmodule Foo do def bar("user_id:" <> id, %{name: name}) do IO.puts("User #{id} has name #{name}") end def bar("admin_id:" <> id, %{name: name}) do IO.puts("Admin #{id} has name #{name}") end def bar(_, _) do IO.puts("Error! What are you?") end end ``` Extract 7 separate values of various sizes and encodings from a string of binary data in 1 expression. ```elixir <<_pad :: size(7)-unit(8), csec :: size(4)-signed-little-integer-unit(8), sec :: size(1)-unit(8), min :: size(4)-unsigned-big-integer-unit(8), deg :: size(1)-unit(8), _pad2 :: size(3)-unit(8), flags :: size(1)-unit(8)>> = data ``` --- # Resources If you like rbenv or rvm, asdf (https://github.com/HashNuke/asdf) is a simple version manager for Elixir (and Erlang, Ruby, even Node). * http://elixir-lang.org/getting-started/introduction.html * Intro to Elixir for Rubyists: https://youtu.be/XbD1Emhm31w * http://elixirschool.com * Elixir Slack: https://elixir-slackin.herokuapp.com Two really great books on the language and how it can be used with Erlang. .pull-left.center[] .pull-right.center[] --- # Go Try It! All kinds of other stuff I haven't (maybe) mentioned: * Rake + Bundler → mix * Macros (metaprogramming) * Protocols * Comprehensions * First-class documentation, available at run-time * Doctests * Concurrent unit tests * Agents * GenServers * Supervision trees * It even has `if` statements, but you'll rarely need them! and some fun projects... .pull-left.center[  http://phoenixframework.org Web framework of choice. ] .pull-right.center[  http://nerves-project.org Nerves, linux firmware booting directly into Erlang VM. ] ??? ### Features * Macros let you manipulate the code that has been passed to them, change how it's evalulated, etc. * Macros are how metaprogramming works in Elixir * A large amount of the language itself is defined as macros * most keywords * most operators ### Phoenix * Web framework, treats traditional controllers and web sockets equally ### Nerves * Embedded stuff, IoT, all code written in Elixir/Erlang * Replaces init with Erlang VM!