Intro to Elixir

by @tednaleid

Intro to Elixir

by @tednaleid

Quick Background

Why should you spend time with Elixir?

Linguistic Relativity

(AKA Sapir-Whorf Hypothesis)
"The structure of a language affects the way in which its respective speakers conceptualize their world." - wikipedia

Covers: Ruby, Io, Prolog, Scala, Erlang, Clojure, Haskell

The Free Lunch is Over

Used 5 criteria to determine the language to use

Natural Syntax

Fast and Repeatable Dev Cycle

Concurrent and Fast

Metaprogramming

Failure Management

Short List Choice #1: Ruby + $$$

Good
Natural Syntax, Fast and Repeatable Dev Cycle, Metaprogramming

Bad
Concurrent and Fast, Failure Management

Short List Choice #2: Erlang

Good
Failure Management, Concurrent and Fast

Bad
Natural Syntax, Fast and Repeatable Dev Cycle, Metaprogramming

Short List Choice #3: Clojure

Good
Concurrent and Fast, Metaprogramming, Fast & Repeatable Dev Cycle

OK
Failure Management

Bad
Natural Syntax

Met José, Found Elixir

"Twisted Love Child of all 3 options"

What is Elixir?

Mostly immutable, functional language with ruby-like syntax running on the battle-tested Erlang BEAM VM

What is Erlang/BEAM?

Language + VM, Created by Ericsson in 1986 to support distributed, fault-tolerant, always-up systems

How does Elixir satisfy 5 criteria?

1. Natural Syntax

Comfortable for most programmers, heavy weighting on quick parsing & readability

Feels Rubyish

Basic Types

iex> my_utf8_string = "hello Iñtërnâtiônàlizætiøn"
"hello Iñtërnâtiônàlizætiøn"
­
iex> my_atom = :hello
:hello
­
iex> 1000 == 1_000
true
­
iex> 0.314159e1 == 314159.0e-5
true
­
iex> my_range = 1..5
1..5

Tuples, Lists, Maps

iex> my_tuple = {:ok, "return value", 715}
{:ok, "return value", 715}
­
iex> my_list = [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
­
iex> my_map = %{:first => "Ted", :last => "Naleid"}
%{first: "Ted", last: "Naleid"}

Closures

iex> sum = fn a, b -> a + b end
#Function<12.90072148/2 in :erl_eval.expr/5>
­
iex> sum.(1, 2)
3
­
# shortened closure syntax:
­
iex> short_sum = &(&1 + &2)
&:erlang.+/2
­
iex> short_sum.(1, 2)
3

Structs

iex> defmodule Person do
...>   defstruct first: "Ted", last: "Naleid"
...> end
{:module, Person, …}
­
iex> %Person{}
%Person{first: "Ted", last: "Naleid"}
­
iex> %Person{first: "Hazel"}
%Person{first: "Hazel", last: "Naleid"}
­
iex> %Person{nope: "Bad Field"}
** (CompileError) iex:4: unknown key :nope for struct

Protocols

iex> defprotocol Stringable do
...>   def to_string(value)
...> end
­
iex> defimpl Stringable, for: Person do
...>   def to_string(value) do
...>     "#{value.first} #{value.last}"
...>   end
...> end
­
iex> Stringable.to_string(%Person{})
"Ted Naleid"

Some similarities to an OOP Interface

Pattern Matching

iex> a = {:ok, 1}
{:ok, 1}  
­
iex> {:ok, b} = {:ok, 1}
{:ok, 1}
­
iex> b
1    

Pattern Matching Lists

iex> [a, b, c] = [1, 2, 3]
[1, 2, 3]
­
iex> a
1
­
iex> [head | tail] = [1, 2, 3]
[1, 2, 3]
­
iex> head
1
­
iex> tail
[2, 3]

Pattern Matching Structs

case HTTP.get(url) do
  {:ok, %HTTP.Resp{ status: 200, body: body }} ->
    IO.puts body
  {:ok, %HTTP.Resp{ status: 404 }} ->
    IO.puts "Not found :("
  {:ok, %HTTP.Resp{ status: status }} ->
    IO.puts "HTTP Status: #{status}"
  {:error, %HTTP.Error{ reason: reason }} ->
    IO.inspect reason
  _ ->
    IO.puts "¯\_(ツ)_/¯"
end     

Even More Pattern Matching

def execute({:ok, good_value}) do
  IO.puts "Known good value: #{good_value}"
end
­
def execute({:error, error_reason}) do
  IO.puts "Error! #{error_reason}"
end
­
iex> execute({:ok, "Yay!"})
Known good value: Yay!
­
iex> execute({:error, "Boo!"})
Error! Boo!

Pipe Operator |>

defmodule Shop do
  defp apply_tax(prices) do 
    Enum.map(prices, fn v -> v * 1.1 end)
  end
­
  def cart_total(items) do
    Enum.sum(
      apply_tax(
        Enum.map(items, fn item -> item.price end)))
  end
end 
­
Shop.cart_total([%{:price=>5.00}, %{:price=>2.00}])
# => 7.7

Nested cart_total is hard to read, have to read inside out

Pipe Operator |>

  def cart_total(items) do
    prices = Enum.map(items, fn itm -> itm.price end)
    prices_with_tax = apply_tax(prices)
    Enum.sum(prices_with_tax)
  end

Intermediate variables cleans up a bit

Pipe Operator |>

def cart_total(items) do
  items
  |> Enum.map(fn item -> item.price end)
  |> add_tax
  |> Enum.sum
end

|> operator passes result from last method as first param in next

Similar to unix pipe: ps ax | grep iex | awk '{ print $1 }'

Simple Interop with Erlang


iex> :crypto.md5("sekr1t")
<<192, 151, 240, 131, 252, 86, 1, 90, 71, 171, 2, …

Can easily leverage 20+ years of Erlang libraries

2. Fast & Repeatable Dev Cycle

Immutability

Very Testable, Easy to Parallelize, No Side Effects

Hot Reloading Without Restarting

Mix Build Tool

$ mix new myapp
* creating README.md
* creating .gitignore
* creating mix.exs
* creating config
* creating config/config.exs
* creating lib
* creating lib/myapp.ex
* creating test
* creating test/test_helper.exs
* creating test/myapp_test.exs
­
Your mix project was created successfully.
You can use mix to compile it, test it, and more:
­
    cd myapp
    mix test
­
Run `mix help` for more commands.

Hex Package Manager

defmodule MyProject.Mixfile do
  use Mix.Project
­
  def project do
    [app: :myapp,
     version: "0.0.1",
     elixir: "~> 1.0",
     deps: deps]
  end   
­
  def application do
    [applications: [:logger]]
  end
­
  defp deps do
    [{:ecto, "~> 0.11.3"},
     {:postgrex, "~> 0.8.1"},
     {:cowboy, github: "extend/cowboy"}]
  end
end

iex is a Great REPL

iex> h Enum.map<tab>
map/2           map_join/3      map_reduce/3
­
iex> h Enum.map/2
­
                            def map(collection, fun)
­
Returns a new collection, where each item is the result of invoking fun 
on each corresponding item of collection.
­
For dicts, the function expects a key-value tuple.
­
Examples
­
┃ iex> Enum.map([1, 2, 3], fn(x) -> x * 2 end)
┃ [2, 4, 6]

┃ iex> Enum.map([a: 1, b: 2], fn({k, v}) -> {k, -v} end)
┃ [a: -1, b: -2]

Debugging via pry

require IEx
­
def index(conn, _params) do
  IEx.pry
  conn |> render "index"
end

Similar to the JavaScript debugger; command

Erlang Activity Monitor

iex> :observer.start

Quick Demo

3. Metaprogramming

How expressive can you make your code

Hygenic Macros

defmacro unless(expr, opts) do
  quote do
    if(!unquote(expr), unquote(opts))
  end
end
­
unless true do
  IO.puts "this will never be seen"
end

Most of the standard library is written using macros

Easy Access to AST

iex> ast = quote, do 2 * 2 / 7
{:/,[context: Elixir, import: Kernel],
 [{:*,[context: Elixir, import: Kernel], [2, 2]}, 7]}

Underlying AST looks a bit like a lisp

Full discussion bigger than this presentation, check out "Metaprogramming Elixir", by Chris McCord

4. Concurrent & Fast

WhatsApp Runs On Erlang VM

At time of Facebook acquisition for $19 Billion

10 Developers

Great Single-Box Scalability

Often 100's of thousands to millions of processes per machine
~2KB of stack/heap per thread

hex.pm on Single 512MB Heroku Dyno

(~0.15 load, from https://twitter.com/emjii/status/591240463782391808)

Concurrency Built-in

iex> parent = self()
#PID<0.90.0>
­
iex> spawn(fn -> send parent, "hello world" end)
#PID<0.93.0>
­
iex> receive do message -> IO.puts message end
hello world
:ok

Isolation/Immutability Allows Garbage Collection at Process Level

No big garbage collection pauses

5. Failure Management

OTP Apps are Supervision Trees

Supervisors monitor/start/stop Child Workers/Supervisors

Elixir/OTP motto: "Let it Crash"

Most Languages Littered with
Defensive Programming

Miss an edge case and your app crashes
ex: Goroutines aren't memory isolated…one dying takes down entire process

Better Architecture -> Cleaner Code

Exception handling is rare

Other Reasons for Picking Elixir

Phoenix Framework

Elixir killer app; most developers weren't interested in
Ruby till Rails & Groovy till Grails

Great, Growing Community

Hex Downloads (from @emjii on 2015-06-24)

Solid Documentation

Elixir's Strengths

Apps with many connections & high uptime requirements

(i.e. Internet of Things, REST/socket webservices, Mobile App back-end, Telephony)

Elixir Weaknesses

Significant Graphics/GUI or Sequential Math

There are better languages for the next Doom or Bitcoin mining

"Three Major Hurdles to Learning"

(from Francesco Cesarini, founder of Erlang Solutions)

1. Functional Programming

Pattern Matching and Tail Recursion

2. Thinking/Reasoning Concurrently

Have a process for every truly concurrent activity in your system

3. Understanding Fault Tolerance and 'Let it Crash' Mentality

The Pragmatic Programmer

"Learn at least one new language every year"

Don't make it one that's the same paradigms that you already know.

That's learning syntax, not better programming

Getting Started

elixir-lang.org

Programming Elixir


Notice the author?

Elixir in Action


Released in June 2015

#elixir-lang on Freenode IRC

Follow @ElixirMN and @elixirlang

@tednaleid

Questions?