تخطَّ إلى المحتوى

Elixir Cheat Sheet

Overview

Elixir is a dynamic, functional programming language designed for building scalable and maintainable applications. It leverages the Erlang VM (BEAM), known for running low-latency, distributed, and fault-tolerant systems. Elixir provides productive tooling and an extensible design, with first-class support for metaprogramming via macros, polymorphism via protocols, and concurrent programming via lightweight processes.

The language was created by Jose Valim and first appeared in 2011. Elixir builds on top of decades of Erlang/OTP battle-tested infrastructure while providing a modern syntax, comprehensive standard library, and excellent developer experience. It powers real-time systems, web applications via the Phoenix framework, embedded systems via Nerves, and data processing pipelines via Broadway and Flow.

Installation

macOS

# Using Homebrew
brew install elixir

# Verify installation
elixir --version
iex --version

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
sudo apt-get update

# Install Erlang and Elixir
sudo apt-get install esl-erlang elixir

Using asdf Version Manager

# Install asdf plugins
asdf plugin add erlang
asdf plugin add elixir

# Install specific versions
asdf install erlang 26.2
asdf install elixir 1.16.1-otp-26

# Set global versions
asdf global erlang 26.2
asdf global elixir 1.16.1-otp-26

Docker

docker run -it --rm elixir:1.16-slim iex

Core Language Features

Data Types

TypeExampleDescription
Integer42, 0xFF, 0b1010Arbitrary precision integers
Float3.14, 1.0e-1064-bit double precision
Atom:ok, :error, trueConstants whose name is their value
String"hello"UTF-8 encoded binaries
List[1, 2, 3]Linked lists
Tuple{:ok, "value"}Fixed-size containers
Map%{key: "value"}Key-value store
Keyword List[name: "Elixir"]Ordered list of atom-keyed tuples
Range1..10Inclusive range
Binary<<1, 2, 3>>Sequence of bytes

Pattern Matching

# Basic pattern matching
{:ok, result} = {:ok, 42}
[head | tail] = [1, 2, 3, 4]

# Function clause matching
defmodule Math do
  def factorial(0), do: 1
  def factorial(n) when n > 0, do: n * factorial(n - 1)
end

# Case expression
case HTTPClient.get(url) do
  {:ok, %{status: 200, body: body}} -> parse(body)
  {:ok, %{status: 404}} -> :not_found
  {:error, reason} -> {:error, reason}
end

# With expression for chaining matches
with {:ok, user} <- fetch_user(id),
     {:ok, profile} <- fetch_profile(user),
     {:ok, avatar} <- fetch_avatar(profile) do
  {:ok, %{user: user, profile: profile, avatar: avatar}}
else
  {:error, reason} -> {:error, reason}
end

Pipe Operator

# Transform data through a pipeline
"Hello, World!"
|> String.downcase()
|> String.split(", ")
|> Enum.map(&String.capitalize/1)
|> Enum.join(" - ")
# => "Hello - World!"

Modules and Functions

defmodule MyApp.User do
  @moduledoc "User management module"

  defstruct [:name, :email, age: 0]

  @doc "Creates a new user"
  def new(name, email) do
    %__MODULE__{name: name, email: email}
  end

  # Private function
  defp validate_email(email) do
    String.contains?(email, "@")
  end

  # Default arguments
  def greet(user, greeting \\ "Hello") do
    "#{greeting}, #{user.name}!"
  end

  # Guard clauses
  def set_age(user, age) when is_integer(age) and age >= 0 do
    %{user | age: age}
  end
end

Enum and Stream

Common Enum Functions

FunctionExampleDescription
map/2Enum.map([1,2,3], &(&1 * 2))Transform each element
filter/2Enum.filter(list, &(&1 > 2))Keep matching elements
reduce/3Enum.reduce(list, 0, &+/2)Accumulate to single value
find/2Enum.find(list, &(&1 > 5))First matching element
sort/2Enum.sort(list, :desc)Sort elements
group_by/2Enum.group_by(users, & &1.role)Group by function result
chunk_every/2Enum.chunk_every(list, 3)Split into chunks
flat_map/2Enum.flat_map(list, &expand/1)Map and flatten
zip/2Enum.zip(keys, values)Pair elements together
uniq_by/2Enum.uniq_by(users, & &1.email)Unique by function

Lazy Streams

# Streams are lazy and composable
1..1_000_000
|> Stream.filter(&(rem(&1, 2) == 0))
|> Stream.map(&(&1 * 3))
|> Stream.take(10)
|> Enum.to_list()

# Infinite streams
Stream.iterate(0, &(&1 + 1))
|> Stream.filter(&(rem(&1, 3) == 0))
|> Enum.take(5)
# => [0, 3, 6, 9, 12]

# File streaming (memory efficient)
File.stream!("large_file.csv")
|> Stream.map(&String.trim/1)
|> Stream.reject(&(&1 == ""))
|> Enum.take(100)

Concurrency with Processes

Spawning Processes

# Spawn a process
pid = spawn(fn -> IO.puts("Hello from process!") end)

# Spawn with link (crash propagation)
pid = spawn_link(fn -> do_work() end)

# Send and receive messages
send(self(), {:hello, "world"})
receive do
  {:hello, msg} -> IO.puts("Got: #{msg}")
after
  5000 -> IO.puts("Timeout!")
end

GenServer

defmodule MyApp.Counter do
  use GenServer

  # Client API
  def start_link(initial \\ 0) do
    GenServer.start_link(__MODULE__, initial, name: __MODULE__)
  end

  def increment, do: GenServer.cast(__MODULE__, :increment)
  def get_count, do: GenServer.call(__MODULE__, :get)

  # Server callbacks
  @impl true
  def init(count), do: {:ok, count}

  @impl true
  def handle_cast(:increment, count), do: {:noreply, count + 1}

  @impl true
  def handle_call(:get, _from, count), do: {:reply, count, count}
end

Task and Agent

# Task for async work
task = Task.async(fn -> expensive_computation() end)
result = Task.await(task, 10_000)

# Multiple concurrent tasks
tasks = Enum.map(urls, fn url ->
  Task.async(fn -> HTTPClient.get(url) end)
end)
results = Task.await_many(tasks, 30_000)

# Agent for simple state
{:ok, pid} = Agent.start_link(fn -> %{} end)
Agent.update(pid, &Map.put(&1, :key, "value"))
Agent.get(pid, &Map.get(&1, :key))

Mix Build Tool

Common Commands

CommandDescription
mix new my_appCreate new project
mix new my_app --supCreate project with supervision tree
mix deps.getFetch dependencies
mix deps.update --allUpdate all deps
mix compileCompile project
mix testRun test suite
mix test --coverRun tests with coverage
mix formatFormat code
mix format --check-formattedCheck formatting (CI)
mix hex.outdatedCheck for outdated deps
mix docsGenerate documentation
mix releaseBuild release

mix.exs Configuration

defmodule MyApp.MixProject do
  use Mix.Project

  def project do
    [
      app: :my_app,
      version: "0.1.0",
      elixir: "~> 1.16",
      start_permanent: Mix.env() == :prod,
      deps: deps(),
      aliases: aliases()
    ]
  end

  def application do
    [
      extra_applications: [:logger],
      mod: {MyApp.Application, []}
    ]
  end

  defp deps do
    [
      {:phoenix, "~> 1.7"},
      {:ecto_sql, "~> 3.11"},
      {:jason, "~> 1.4"},
      {:ex_doc, "~> 0.31", only: :dev, runtime: false},
      {:credo, "~> 1.7", only: [:dev, :test], runtime: false}
    ]
  end

  defp aliases do
    [
      setup: ["deps.get", "ecto.setup"],
      "ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
      "ecto.reset": ["ecto.drop", "ecto.setup"],
      test: ["ecto.create --quiet", "ecto.migrate --quiet", "test"]
    ]
  end
end

Configuration

Runtime Configuration

# config/config.exs
import Config

config :my_app,
  ecto_repos: [MyApp.Repo]

config :my_app, MyApp.Repo,
  database: "my_app_dev",
  hostname: "localhost"

# config/runtime.exs (runtime config)
import Config

if config_env() == :prod do
  config :my_app, MyApp.Repo,
    url: System.get_env("DATABASE_URL"),
    pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
end

Advanced Usage

Behaviours and Protocols

# Define a behaviour
defmodule MyApp.Cache do
  @callback get(key :: String.t()) :: {:ok, term()} | :miss
  @callback put(key :: String.t(), value :: term(), ttl :: integer()) :: :ok
end

# Implement a protocol
defprotocol MyApp.Renderable do
  @doc "Renders a data structure to string"
  def render(data)
end

defimpl MyApp.Renderable, for: Map do
  def render(map), do: Jason.encode!(map)
end

Metaprogramming with Macros

defmodule MyApp.Retry do
  defmacro with_retry(opts \\ [], do: block) do
    max = Keyword.get(opts, :max_attempts, 3)
    quote do
      Enum.reduce_while(1..unquote(max), nil, fn attempt, _acc ->
        try do
          {:halt, unquote(block)}
        rescue
          e -> if attempt == unquote(max), do: {:halt, {:error, e}}, else: {:cont, nil}
        end
      end)
    end
  end
end

Troubleshooting

ProblemSolution
(UndefinedFunctionError)Ensure module is compiled; check function arity
(MatchError)Pattern doesn’t match value; add catch-all clause
(Protocol.UndefinedError)Implement protocol for the given type
Process crash cascadingUse Supervisors with proper restart strategies
Memory growing unboundedCheck for process mailbox buildup; use Process.info/2
Slow compilationCheck for circular module dependencies
mix deps.get failuresClear _build and deps dirs, retry
OTP version mismatchEnsure Erlang/OTP version matches elixir_make requirements