Introduction to Elixir
This chapter gets you from zero to a running Elixir program, with enough context to understand what makes the language different.
What is Elixir?
Elixir is a dynamic, functional language that runs on the Erlang VM (BEAM). The BEAM was built for telecom switches, so it does low-latency, distributed, and fault-tolerant work the way some VMs do garbage collection: as a default rather than a feature.
Key Features
- Functional: immutable data, first-class functions, pattern matching
- Concurrent: lightweight processes (not OS threads), spawn millions on one machine
- Fault-tolerant: the "let it crash" philosophy plus supervisor trees
- Scalable: distributed by design, runs across multiple nodes
- Productive: modern syntax, powerful metaprogramming, good tooling
- Interactive: IEx is the REPL you'll live in while learning
History
- Created by José Valim in 2011
- Built on Erlang VM (Ericsson, 1986)
- Designed to keep Erlang's strengths while fixing the ergonomics
- Used in production at Discord, Pinterest, Bleacher Report, Financial Times
Installation
Using asdf (Recommended)
asdf allows you to manage multiple runtime versions.
# Install asdf (visit asdf-vm.com for OS-specific instructions)
# Add Erlang and Elixir plugins
asdf plugin add erlang
asdf plugin add elixir
# Install latest versions
asdf install erlang latest
asdf install elixir latest
# Set global versions
asdf global erlang <version>
asdf global elixir <version>
macOS
# Using Homebrew
brew install elixir
Ubuntu/Debian
# Add Erlang Solutions repo
wget https://packages.erlang-solutions.com/erlang-solutions_2.0_all.deb
sudo dpkg -i erlang-solutions_2.0_all.deb
# Install
sudo apt-get update
sudo apt-get install elixir
Windows
Download installer from elixir-lang.org
Verify Installation
elixir --version
You should see output like:
Erlang/OTP 26 [erts-14.2.1] [...]
Elixir 1.16.0 (compiled with Erlang/OTP 26)
Your First Elixir Program
Option 1: Script File
Create a file hello.exs:
IO.puts("Hello, World!")
Run it:
elixir hello.exs
Output:
Hello, World!
Option 2: Interactive Shell (IEx)
IEx is Elixir's interactive shell, the equivalent of Python's REPL or Node's console.
iex
Try some commands:
iex> IO.puts("Hello, World!")
Hello, World!
:ok
iex> 1 + 2
3
iex> "hello" <> " " <> "world"
"hello world"
iex> String.upcase("elixir")
"ELIXIR"
IEx Tips
# Get help
iex> h String.upcase
# Autocomplete - press Tab
iex> String.up<TAB>
# Multiline input - use \
iex> 1 + \
...> 2
3
# Get type information
iex> i "hello"
# Shows detailed info about the string
# Previous result with `v`
iex> 1 + 2
3
iex> v()
3
iex> v() * 2
6
# Exit IEx
iex> exit # or Ctrl+C twice
Basic Syntax Overview
Comments
# This is a single-line comment
# There are no multi-line comments in Elixir
# Just use multiple single-line comments
Basic Types
# Integer
42
# Float
3.14
# Boolean
true
false
# Atom (like Ruby symbols or enum values)
:ok
:error
:hello
# String (UTF-8)
"Hello, World!"
# List
[1, 2, 3]
# Tuple
{:ok, "result"}
# Map
%{name: "Alice", age: 30}
Basic Operations
# Arithmetic
iex> 1 + 2
3
iex> 10 - 5
5
iex> 3 * 4
12
iex> 10 / 2
5.0 # Always returns float
iex> div(10, 3) # Integer division
3
iex> rem(10, 3) # Remainder
1
# String concatenation
iex> "Hello" <> " " <> "World"
"Hello World"
# String interpolation
iex> name = "Alice"
"Alice"
iex> "Hello, #{name}!"
"Hello, Alice!"
# Boolean operators
iex> true and false
false
iex> true or false
true
iex> not true
false
# Comparison
iex> 1 == 1
true
iex> 1 != 2
true
iex> 1 < 2
true
iex> 1 >= 1
true
# Strict equality (checks type too)
iex> 1 == 1.0 # true (values are equal)
true
iex> 1 === 1.0 # false (different types)
false
Running Elixir Code
Three Ways to Execute
| Method | Use Case | Command |
|---|---|---|
| Script | Run a .exs file | elixir script.exs |
| IEx | Interactive exploration | iex |
| Mix Project | Full application | mix run |
Script Files
Elixir uses two file extensions:
.exs- Elixir Script (interpreted, not compiled).ex- Elixir source (compiled into .beam files)
For learning, use .exs scripts:
# calculate.exs
defmodule Calculator do
def add(a, b), do: a + b
def multiply(a, b), do: a * b
end
IO.puts("3 + 4 = #{Calculator.add(3, 4)}")
IO.puts("3 * 4 = #{Calculator.multiply(3, 4)}")
Run:
elixir calculate.exs
Output:
3 + 4 = 7
3 * 4 = 12
Loading Code in IEx
# Load a file into IEx
iex> c "calculate.exs"
[Calculator]
# Now you can use the module
iex> Calculator.add(5, 3)
8
Your First Module
# greeter.exs
defmodule Greeter do
def hello(name) do
"Hello, #{name}!"
end
def goodbye(name) do
"Goodbye, #{name}!"
end
end
# Call the functions
IO.puts Greeter.hello("World")
IO.puts Greeter.goodbye("World")
Run:
elixir greeter.exs
Output:
Hello, World!
Goodbye, World!
Elixir Philosophy
1. Let It Crash
Most languages train you to wrap every risky call in a try/catch. Elixir flips that. Crash early, supervise the restart, keep the rest of the system running.
# Don't pad every function with defensive checks.
# Let a process crash and let its supervisor restart it.
# Clearer code, fewer hidden states.
2. Immutability
Data is immutable. Variables don't mutate, they rebind.
list = [1, 2, 3]
new_list = [0 | list] # Creates new list: [0, 1, 2, 3]
# Original list unchanged: [1, 2, 3]
3. Pattern Matching
The = operator is not assignment. It's a match. Both sides have to fit, or you get an error.
{:ok, result} = {:ok, 42}
# result is now 42
{:ok, result} = {:error, "failed"}
# ** (MatchError) - pattern doesn't match!
4. Transformation Pipeline
Data flows through transformations:
"hello world"
|> String.upcase()
|> String.split()
# => ["HELLO", "WORLD"]
Common Gotchas for Beginners
1. Variables Don't Vary (Much)
x = 1
x = 2 # This is actually rebinding, not mutation
# The original value 1 still exists elsewhere
2. Division Returns Float
10 / 2 # => 5.0 (not 5)
div(10, 2) # => 5 (integer division)
3. Atoms Are Not Strings
:hello != "hello" # Different types
4. Lists Are Linked Lists
# Fast operations
[0 | list] # Prepend: O(1)
# Slow operations
list ++ [4] # Append: O(n)
length(list) # Length: O(n)
# Use Enum.count/1 for any enumerable
5. Parentheses Are Optional (But Use Them)
# Both work
String.upcase("hello")
String.upcase "hello"
# But be consistent (use parentheses for clarity)
Next Steps
Continue to 02-basics.md to learn about variables, types, and pattern matching in depth.
Exercise
Try these in IEx to get comfortable:
- Calculate the area of a circle with radius 5 (π * r²)
- Create a string with your name and use
String.length/1to count characters - Create a list
[1, 2, 3]and prepend 0 to it - Use string interpolation to create "Hello, #{your_name}!"
- Try using the pipe operator:
"HELLO" |> String.downcase() |> String.reverse()
# Solutions
iex> 3.14159 * 5 * 5
78.53975
iex> name = "Alice"
"Alice"
iex> String.length(name)
5
iex> [0 | [1, 2, 3]]
[0, 1, 2, 3]
iex> "Hello, #{name}!"
"Hello, Alice!"
iex> "HELLO" |> String.downcase() |> String.reverse()
"olleh"