Intro to Elixir
What is Elixir?
Elixir is a dynamic, functional language designed for building scalable and
maintainable applications.
Elixir leverages the Erlang VM, known for running low-latency, distributed and
fault-tolerant systems, while also being successfully used in web development,
embedded software, data ingestion, and multimedia processing domains.
What is Elixir?
● Functional programming
○ Function matching
○ Tuples in responses
● Based on Erlang
○ Benefits of Erlang
○ Readability of Ruby
● Dependency manager baked into the language
○ Mix (+Hex)
Elixir vs Java
● Both use a VM (BEAM vs JVM)
● Erlang is like Java while Elixir is like Groovy
● Functional vs Object Oriented
Elixir vs Java
# Elixir
// Java
defmodule Course do
public class Course {
defp main(args \\ []) do
public static void main(String[] args) {
IO.puts(“hello world”)
System.out.println(“hello world”)
end
}
end
}
Syntax
● No semicolons (;)
defmodule Apollo.Utils.Connection do
● Instead of using ({}), instead defp create_url(host, endpoint, params) do
uses do and end keywords query = URI.encode_query(params)
for blocks if query == "" do
● Uses 2 spaces for indentation "#{host}/#{endpoint}"
else
● Uses snake_case naming "#{host}/#{endpoint}?#{query}"
scheme for variables and end
end
methods, but PascalCase for end
modules.
Variables & Types
Elixir is not a strongly typed language, the compiler interprets them.
course = 1
IO.puts(course)
course = “test”
IO.puts(course)
There are a few data types in Elixir:
● Numbers
○ Integers & Decimals (floats)
● Strings
○ Strings & Atoms
● Booleans
Operators
The following operators are used for numbers
● Addition (+)
● Sustraction (-)
● Multiplication (*)
● Division (/)
Operators
For strings, you can use <> to concatenate two string, or use String interpolation
using #{}
hello = "hello"
world = "world"
test_1 = hello <> world
test_2 = "#{hello} #{world}"
Operators
Comparison operators
● Equals (==)
● Not equals (!=)
● More than (>)
● Less than (<)
● More or equals to (>=)
● Less or equals to (<=)
Logic operators
● And (and)
● Or (or)
● Not (not)
Conditionals
Elixir uses if else
if x >= 0 do
“positive”
else
“negative”
end
Case is another special conditional
case x do
0 -> “zero”
n when n > 0 -> “positive”
_n -> “negative”
end
Lists & Tuples
Lists are used instead of arrays.
example = [1, 2, 3, 4, 5]
Enum.at(example, 1)
# 2
Tuples are like arrays, but are not mutable.
example = {:ok, 1}
Enum.at(example, 1)
# 1
Functions
Functions are defined inside a module
defmodule Test do
def public_function do
“hello”
end
defp private_function do
“world”
end
end
Test.public_function()
Test.private_function()
Note: The functions last line will be returned.
Map & Reduce
As Elixir is a functional language, instead of using loops you use these functions.
Map goes over the lists and transforms each element
numbers = [1, 2, 3, 4, 5]
numbers_2 = Enum.map(numbers, fn n -> n * 2 end)
Enum.reduce(numbers_2, 0, fn n, sum -> sum + n end)
Enum.reduce([“a”, “b”], %{}, fn letter, map -> Map.put(map, letter, 0) end)
# %{“a” => 0, “b” => 0}
Reduce makes the list into a single value
numbers = [1, 2, 3, 4, 5]
Enum.reduce(numbers, 0, fn n, sum -> sum + n end)
# 15
Pattern Matching
Pattern matching is useful for functions definitions and destructuring return values
def do_something({:ok, _no_error}), do: “ok”
def do_something({:error, error}), do: “error”
# def do_somethings(%User{} = user), do: “ok”
# def do_something(nil), do: “error”
# def do_something(_default), do: “error”
def only_numbers(n) when is_number(n), do: n * 2
def only_numbers(_), do: {:error, “not a number”}
[start, end] = String.split(“start|end”, “|”)
{a, b} = some_function()
Linter
● Use Credo to get recommendations and best practices.
APIs with Elixir
Elixir + Phoenix
● Phoenix is like Ruby on Rails
○ Old post I made about Rails to Phoenix
○ API Only
● Why?
○ More efficient
○ Widely adopted
● MVC framework
○ Model
■ Structs (+ Ecto)
○ View
■ Phoenix Views (+ JA Serializer)
○ Controller
■ Phoenix controllers (+ Fallback controllers)
Ecto
● An ORM
○ Multi database support
○ Different Adapters
● Queries
○ Keyword syntax
○ Macro syntax
○ Raw SQL
Phoenix Contexts
● Domain driven design
● Separation of Logic & Web layers
○ Web helpers vs Logic Helpers
● Scalability via Separation
○ Contexts -> Umbrella Apps -> Microservices
● Singular API to access business logic
○ Accounts.create_user/1 vs Accounts.Users.create/1
What goes where?
● Fat Controllers?
● Fat Models?
● Services?
def update(conn, %{"id" => id, "data" => params}) do
with {:ok, benefit} <- Insurance.find_benefit(id),
{:ok, benefit} <- Insurance.update_benefit(benefit, params)
do
render(conn, "show.json-api", data: benefit)
end
end
Non RESTful routes?
● When?
○ Non resource routes, ie webhooks
○ Actions, ie split, unsubscribe
● How?
○ Namespaces
○ Subroutes
# Member
resources "/charges", ChargeController, only: [:show, :update, :create, :delete] do
put "/split", ChargeController, :split
end
# Collection
post "/products/validate", ProductController, :validate
resources "/products", ProductController, only: [:index]
Questions?
Tutorial
Learning Phoenix