Elixir Cheatsheet

Functions and Modules

Use this Elixir reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Module Shape

defmodule Greeter do
  @moduledoc "Greeting helpers."

  def hello(name) do
    "Hello, #{name}"
  end

  defp normalize(name), do: String.trim(name)
end

Greeter.hello("Ada")

defmodule defines a module. def defines a public function, defp a private one. Nest namespaces with dots: defmodule MyApp.Accounts.User.

alias, import, require, use

defmodule MyApp.Billing do
  alias MyApp.Accounts.User          # now just User
  alias MyApp.Accounts.{User, Team}  # multi-alias
  alias MyApp.Repo, as: DB           # rename

  import Ecto.Query, only: [from: 2] # call from/2 without the module prefix
  import String, except: [length: 1]

  require Logger                     # needed before using a module's MACROS
  Logger.info("compiled")

  use GenServer                      # runs GenServer.__using__/1: injects code
end

Rule of thumb: alias for shorter names (default choice), import sparingly and with :only, require for macros (Logger, Integer.is_even/1), use when a library tells you to (it injects callbacks, imports, and behaviour wiring).

Module Attributes

defmodule Circle do
  @moduledoc "Circle math."          # module docs (heredoc for long text)

  @pi 3.14159                        # compile-time constant
  @timeout :timer.seconds(5)         # evaluated at compile time

  @doc "Area from a radius."
  @spec area(number()) :: float()
  def area(r), do: @pi * r * r
end

@doc/@moduledoc feed h Circle.area in IEx and hexdocs. @spec documents types for Dialyzer: @spec fetch(String.t()) :: {:ok, map()} | {:error, atom()}. Define custom types with @type user_id :: pos_integer(). Attributes are inlined at compile time — they are constants, not mutable state.

Arity

Functions are identified by module, name, and arity:

String.length("abc")                 # String.length/1
Enum.map([1, 2], fn x -> x * 2 end)  # Enum.map/2

same/1 and same/2 are different functions and can coexist.

One-line Functions

def double(x), do: x * 2
def zero?(0), do: true
def zero?(_), do: false

Use block form when the body needs several expressions.

Multiple Clauses and Guards

defmodule MathList do
  def sum([]), do: 0
  def sum([head | tail]), do: head + sum(tail)

  def describe(n) when is_integer(n) and n > 0, do: :positive
  def describe(0), do: :zero
  def describe(_), do: :other
end

The first matching clause runs. Clauses of the same function must be adjacent.

Anonymous Functions

double = fn x -> x * 2 end
double.(10)                       # called with a dot: fun.(args)

add = fn a, b -> a + b end

handle = fn
  {:ok, value} -> value           # anonymous functions can have clauses
  {:error, _} -> nil
end

Capture Shorthand

Enum.map([1, 2, 3], &(&1 * 2))
Enum.reduce([1, 2, 3], 0, &+/2)
Enum.map(["a ", " b"], &String.trim/1)   # &Mod.fun/arity captures a named function
parse = &Integer.parse/1

&1, &2, … refer to arguments. Prefer a full fn when the body is more than one small expression.

Default Arguments

def greet(name, prefix \\ "Hello") do
  "#{prefix}, #{name}"
end

When combining defaults with multiple clauses, declare a bodiless function head first:

def greet(name, prefix \\ "Hello")
def greet(name, prefix), do: "#{prefix}, #{name}"

Keyword Options

def search(query, opts \\ []) do
  limit = Keyword.get(opts, :limit, 10)
  sort  = Keyword.get(opts, :sort, :asc)
  {query, limit, sort}
end

search("elixir", limit: 5)   # trailing keyword list needs no brackets

An options keyword list as the last argument is the standard Elixir API shape (Enum.sort/2, String.split/3, GenServer options all use it).