Elixir

“If Java is 'write once, run anywhere', then Erlang is 'write once, run forever'.”

- Joe Armstrong

“ The AXD301 has achieved a NINE nines reliability (yes, you read that right, 99.9999999%). Let’s put this in context: 5 nines is reckoned to be good (5.2 minutes of downtime/year). 7 nines almost unachievable ... but we did 9.

Why is this? No shared state, plus a sophisticated error recovery model. “

- Joe Armstrong

“ Last June a presentation at the Code BEAM conference noted that every year Cisco ships about two million devices with Erlang, and that 90% of all internet traffic goes

through Erlang controlled nodes “

- Joe Armstrong

Varför Elixir?

Erlang är gammalt :(

Erlang |> Ruby :: ❤ Elixir ❤

  • immutability

  • pipes

  • concurrency

  • scalability

  • sigils

  • function guards

  • underbar syntax

  • pattern matching

  • meta-programming

  • readable regex

  • doctests

    “Programming should be about transformation of data.”

# Utan pipes
String.replace(String.trim(String.downcase("      HEJSAN!  ")), "san!", "")

# Med pipes
"      HEJSAN!        "
  |> String.downcase()
  |> String.trim()
  |> String.replace("san!", "")
# hej

Pipes

def get_user_data(user) do
  Api.get_data(user)
  |> filter_sensitive_info()
  |> IO.inspect()
  |> add_timestamps()
  |> downcase_username()
end

Erlang |> Ruby :: ❤ Elixir ❤

  • immutability

  • pipes

  •  

  • concurrency

  • scalability

  • sigils

  • function guards

  • underbar syntax

  • meta-programming

  • readable regex

  • doctests

  • pattern matching

Rethinking assignment

user = {"Alexander", "iteam"}

{"Alexander", employer} = user
IO.puts(employer)
# iteam

{"Andreas", employer} = user
# == Compilation error in file lib/test.ex ==
# ** (MatchError) no match of right hand side value: {"Andreas", "iteam"}
# lib/test.ex:22: Test.greet/0
# (elixir) lib/kernel/parallel_compiler.ex:229: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/7

Pattern matching

def greet(), do: IO.puts "hejsan anonym!"
def greet("Stefan"), do: IO.puts "Nämen, hallå hallå!"
def greet("Christian"), do: IO.puts "God afton direktören!"


greet()
# hejsan anonym!

greet("Stefan")
# Nämen, hallå hallå!

greet("Christian")
# God afton direktören!

def greet([]), do: IO.puts "Det finns ingen att hälsa på!"
def greet([first | tail]), 
  do: IO.puts "Nämen, hallå #{first} och #{tail}.."

greet([])
# Det finns ingen att hälsa på :(

greet(["Andreas", "Kristoffer", "Maria", "Izabella"])
# Nämen, hallå Andreas och KristofferMariaIzabella..



def greet(["Stefan" | tail]), 
  do: IO.puts "Nämen, hallå Stefan och #{tail}.."
def greet([_first | _tail]), 
  do: IO.puts "Jag vill hälsa på Stefan först ju.."

greet(["Emma-Klara", "Maria", "Izabella", "Stefan"])
# Jag vill hälsa på Stefan först ju..

greet(["Stefan", "Emma-Klara", "Maria", "Izabella"])
# Nämen, hallå Stefan och Emma-KlaraMariaIzabella..
def greet([]), do: :ok

def greet([first | []]),
  do: IO.puts "Sist men inte minst, hallå #{first}!"

def greet([first | tail]) do
  IO.puts "Hallå #{first}!"
  greet(tail)
end


greet(["Emil", "Mikaela", "Erik", "Alexander"])
# Hallå Emil!
# Hallå Mikaela!
# Hallå Erik!
# Sist men inte minst, hallå Alexander!

Pattern matching

(lite) mer användbart

use-case

Railway Oriented

def create(params) do

  %User{}
    |> parse_firstname(params["firstname"])
    |> parse_lastname(params["lastname"])
    |> parse_age(params["age"])

end

def parse_lastname({:error, _} = err, _name), do: err
def parse_lastname(user, nil), do: {:error, "name is required"}
def parse_lastname(user, ""), do: parse_lastname(user, nil)

def parse_lastname(user, name), do: %{user | lastname: name}



def create(params) do
  with {:ok, firstname} <- parse_firstname(params["firstname"]),
    {:ok, lastname} <- parse_lastname(params["lastname"])
    {:ok, age} <- parse_age(params["age"])
     
  do
    %User{firstname: firstname, lastname: lastname, age: age }
  else
    err -> err
  end
end


def parse_lastname(nil), do: {:error, "name is required"}
def parse_lastname(""), do: parse_lastname(nil)

def parse_lastname(name), do: {:ok, name}

Doc tests

defmodule Test do

  @doc ~S"""
  Says Hallå to the first element of the list

  ## Examples

    iex> Test.greet(["Rebecca"])
    "Hallå Rebecca!"

    iex> Test.greet(["Martin", "Rebecca"])
    "Hallå Martin!"

  """
  def greet([first | _]) do
    "Hallå #{first}!"
  end
end

Underbar Syntax

### Språkkonventioner från verkliga livet
def is_night? (argument) do
  argument === "MOON"
end

def do_the_thing! do
  raise "Not possible"
end


### function guards

def is_night? (argument) when is_string(argument) do
  argument === "MOON"
end

def is_night? (_)
  false
end


### Sigils

naive_date = ~N{2000-01-01 23:00:07}
# Ett Date-object

~r{foo|bar}
# Regex

~w{foo bar baz}
# ["foo", "bar", "baz"]

### Inga instance-methods, konsekvent och lätt att pipe:a
Enum.map(...)

String.replace(...)

"Just let it crash"

- Unknown

 

  • Self healing

  • Distributed Load

  • Scalable

När?

  • Ett sundare språk än JavaScript
  • Mer skalbart
  • Slipper extra services

När ska man inte använda det?

Phoenix

Ruby on Rails 2.0 (bildligt talat)

  • Mycket av communityn från RoR
  • "database abstraction layer, Templates, Scaffolding a-la RoR
  • Live View
  • Explicit > Implicit
  • Concurrency as a feature

Slutord

Tack!

Om du vill veta mer:

 

How Discord scaled Elixir to 5 000 000 concurrent users:

https://blog.discordapp.com/scaling-elixir-f9b8e1e7c29b

Compiling Elixir to WebAssembly:

https://github.com/lumen/lumen

PhoenixPhrenzy:

https://phoenixphrenzy.com/

Programming Elixir, Dave Thomas

 

deck

By Mikael Gråborg

deck

  • 157