Elixir - Complete Beginner’s Guide
What is Elixir? Elixir is a modern functional language that runs on the Erlang Virtual Machine. Built for building scalable, fault-tolerant systems. Used by Discord, Slack, and more! Elegant and powerful!
1. Getting Started
Your First Program
IO.puts("Hello, Elixir!")
# Output: Hello, Elixir!Comments
# Single line comment
# Multi-line comment
# Just use multiple single lines2. Basic Data Types
Numbers and Strings
# Numbers
42 # Integer
3.14 # Float
1_000_000 # Underscores for readability
# Strings (UTF-8)
"Hello"
"Hello, World!"
# String interpolation
name = "Alice"
"Hello, #{name}!" # "Hello, Alice!"
# Atoms (constants where name is value)
:ok
:error
:hello
# Lists
[1, 2, 3, 4, 5]
[1, "two", 3.0, :four] # Mixed types
# Tuples (fixed size)
{:ok, "Success"}
{"Name", "Alice", 25}
# Maps (key-value pairs)
%{"name" => "John", "age" => 30}
%{"a" => 1, "b" => 2}3. Lists and Pattern Matching
Working with Lists
list = [1, 2, 3, 4, 5]
# List operations
hd(list) # 1 (head - first element)
tl(list) # [2, 3, 4, 5] (tail - rest)
length(list) # 5
# Prepend (fast)
[0 | list] # [0, 1, 2, 3, 4, 5]
# Append (slower)
list ++ [6, 7] # [1, 2, 3, 4, 5, 6, 7]Pattern Matching (Core Feature!)
# Match values
{a, b} = {1, 2}
# a = 1, b = 2
# Ignore values
{_, value} = {1, 2}
# value = 2
# List patterns
[head | tail] = [1, 2, 3]
# head = 1, tail = [2, 3]
[first, second | rest] = [1, 2, 3, 4, 5]
# first = 1, second = 2, rest = [3, 4, 5]
# Pattern matching in functions
def greet({:hello, name}) do
"Hello, #{name}!"
end
greet({:hello, "Alice"}) # "Hello, Alice!"4. Functions
Defining Functions
# Basic function
def add(a, b) do
a + b
end
# Shorter syntax
def multiply(a, b), do: a * b
# Function with pattern matching
def classify(0), do: "Zero"
def classify(n) when n > 0, do: "Positive"
def classify(n) when n < 0, do: "Negative"
# Default parameters
def greet(name \\ "Guest") do
"Hello, #{name}!"
end
greet() # "Hello, Guest!"
greet("John") # "Hello, John!"Anonymous Functions (Lambdas)
# Anonymous function
add = fn a, b -> a + b end
add.(5, 3) # 8
# Shorthand syntax
double = &(&1 * 2)
double.(5) # 10
# Multiple clauses
sum = fn
{:ok, a} -> a
{:error} -> 0
end
sum.({:ok, 42}) # 425. Control Flow
If/Else
age = 18
if age >= 18 do
"You can vote"
else
"You're too young"
end
# Unless (opposite of if)
unless age < 18 do
"You can vote"
end
# Cond (like if-elsif-else)
cond do
age < 13 -> "Child"
age < 18 -> "Teenager"
true -> "Adult"
end
# Case (pattern matching)
case {:ok, "Success"} do
{:ok, message} -> "Received: #{message}"
{:error, reason} -> "Error: #{reason}"
_ -> "Unknown"
end6. Loops and Iteration
Recursion (Primary Loop Method)
# Sum a list recursively
def sum([]), do: 0
def sum([head | tail]), do: head + sum(tail)
sum([1, 2, 3, 4, 5]) # 15
# Countdown
def countdown(0), do: IO.puts("Done!")
def countdown(n) do
IO.puts(n)
countdown(n - 1)
end
countdown(3)
# Prints: 3, 2, 1, Done!Higher-Order Functions
# Enum module (super useful!)
list = [1, 2, 3, 4, 5]
# Map - transform each element
Enum.map(list, fn x -> x * 2 end) # [2, 4, 6, 8, 10]
Enum.map(list, &(&1 * 2)) # Shorthand
# Filter - keep matching
Enum.filter(list, fn x -> x > 2 end) # [3, 4, 5]
Enum.filter(list, &(&1 > 2)) # Shorthand
# Reduce - combine all
Enum.reduce(list, 0, fn x, acc -> acc + x end) # 15
Enum.sum(list) # Easier way: 15
# Each - do something for each
Enum.each(list, fn x -> IO.puts(x) end)
# Find - first match
Enum.find(list, fn x -> x > 3 end) # 47. Strings and Atoms
String Operations
text = "Hello World"
# Length
String.length(text) # 11
# Case conversion
String.upcase(text) # "HELLO WORLD"
String.downcase(text) # "hello world"
# Splitting
String.split(text, " ") # ["Hello", "World"]
# Contains/starts with
String.contains?(text, "World") # true
String.starts_with?(text, "Hello") # true
# Concatenation
text1 = "Hello"
text2 = "World"
text1 <> " " <> text2 # "Hello World"
# String to atom
String.to_atom("hello") # :hello
# Atoms to string
Atom.to_string(:hello) # "hello"8. Maps and Keyword Lists
Maps (Dictionaries)
# Create map
person = %{"name" => "John", "age" => 30}
person = %{name: "John", age: 30} # With atom keys
# Access
person["name"] # "John"
person.name # "John" (if using atoms)
# Update
%{person | name: "Jane"} # Creates new map with updated name
Map.put(person, :email, "jane@example.com")
# Get all keys/values
Map.keys(person) # [:name, :age]
Map.values(person) # ["John", 30]Keyword Lists
# Keyword list (list of tuples)
options = [name: "John", age: 30, city: "NYC"]
# Access
options[:name] # "John"
# Common in function options
def greet(name, options \\ []) do
greeting = options[:greeting] || "Hello"
"#{greeting}, #{name}!"
end9. Modules and Functions
Organizing Code
defmodule Calculator do
def add(a, b) do
a + b
end
def subtract(a, b) do
a - b
end
end
# Call functions
Calculator.add(5, 3) # 8
Calculator.subtract(10, 4) # 6
# Nested modules
defmodule Math.Geometry do
def area_circle(radius) do
3.14 * radius * radius
end
end
Math.Geometry.area_circle(5) # 78.510. Pipes (Elixir Magic!)
Chaining Operations
# Without pipes (nested)
Enum.reverse(Enum.filter(Enum.map([1,2,3,4,5], &(&1*2)), &(&1>5)))
# With pipes (readable!)
[1,2,3,4,5]
|> Enum.map(&(&1*2))
|> Enum.filter(&(&1>5))
|> Enum.reverse()
# Result: [10, 8]
# Real-world example
"hello world"
|> String.upcase()
|> String.split(" ")
|> Enum.join("-")
# Result: "HELLO-WORLD"11. Practical Examples
Count Word Frequency
def count_words(sentence) do
sentence
|> String.downcase()
|> String.split()
|> Enum.frequencies()
end
count_words("hello world hello elixir hello")
# %{"hello" => 3, "world" => 1, "elixir" => 1}Check Password Strength
def password_strength(password) do
length = String.length(password)
has_upper = String.match?(password, ~r/[A-Z]/)
has_lower = String.match?(password, ~r/[a-z]/)
has_digit = String.match?(password, ~r/\d/)
cond do
length < 8 -> "Too short"
not (has_upper and has_lower and has_digit) -> "Weak"
true -> "Strong"
end
end
password_strength("Pass123") # "Strong"
password_strength("weak") # "Too short"Transform Data
users = [
%{name: "Alice", age: 25, active: true},
%{name: "Bob", age: 30, active: false},
%{name: "Charlie", age: 22, active: true}
]
# Get names of active users
users
|> Enum.filter(&(&1.active))
|> Enum.map(&(&1.name))
# ["Alice", "Charlie"]12. Key Concepts
Immutability
# Everything is immutable
list = [1, 2, 3]
new_list = list ++ [4] # Creates new list
# list is still [1, 2, 3]!
# Rebinding is OK
x = 1
x = 2 # New binding, not mutationPattern Matching in Functions
# Great for handling different cases
def process({:ok, data}), do: "Got: #{data}"
def process({:error, reason}), do: "Error: #{reason}"
process({:ok, "Data"}) # "Got: Data"
process({:error, "Failed"}) # "Error: Failed"