liminfo

Elixir Reference

Free reference guide: Elixir Reference

30 results

About Elixir Reference

The Elixir Reference is a searchable cheat sheet covering the core language features of Elixir, organized into six categories. Basic Syntax includes variable binding with immutable rebinding semantics, atoms as lightweight symbolic constants (:ok, :error, true), string interpolation with "#{}", the list/tuple distinction with head/tail destructuring, map literals with %{} and structural update syntax, conditional expressions (if/else, cond for multi-clause, case for value matching), function definition with def (public) and defp (private), and the Enum module for functional list operations like map, filter, and reduce. Every entry shows idiomatic Elixir code with realistic examples.

The Pattern Matching category highlights one of Elixir's most distinctive features. The = operator is a match operator, not assignment — it destructures the right side against the left pattern, binding variables. This applies to tuples ({:ok, result} = {:ok, 42}), lists ([head | tail] = [1,2,3]), and maps (%{name: name} = user). The case expression uses pattern matching across multiple clauses including nested struct patterns. Function clause pattern matching enables clean multi-clause function definitions (factorial/0 and factorial/n). The pin operator (^) prevents rebinding and forces value equality in a match. The with special form chains multiple match operations with a clean else handler, ideal for multi-step operations that can fail. The Processes category covers Elixir's actor-model concurrency: spawn for creating isolated processes, send/receive for message passing with pattern matching on the receive block, Task.async/await for parallel workloads, Agent as a simple key-value state wrapper, and spawn_link plus Process.monitor for crash detection and fault tolerance.

The GenServer category shows Elixir's OTP building block for stateful server processes: defining a GenServer with use GenServer, implementing init/1 for initial state, handle_call/3 for synchronous request-response (reply with {:reply, value, new_state}), handle_cast/2 for fire-and-forget messages ({:noreply, new_state}), handle_info/2 for arbitrary messages (including timer ticks from Process.send_after), and starting a supervision tree with Supervisor.start_link and one_for_one strategy. The Pipes and Lazy Evaluation category covers the |> pipe operator for composing transformations left-to-right, the & capture operator for anonymous function shorthand, and Stream for lazy enumeration that avoids materializing intermediate lists on large datasets. The Macros category explains Elixir's compile-time metaprogramming: defmacro for defining macros that transform AST, quote/unquote for code quoting and interpolation, use for invoking module __using__ hooks, and @module attributes for compile-time constants and documentation.

Key Features

  • Basic syntax: variable binding, atoms, string interpolation, list/tuple, map update syntax, if/cond/case, def/defp, Enum.map/filter/reduce
  • Pattern matching: = match operator, tuple/list/map destructuring, case patterns, function clause matching, pin operator (^)
  • with special form for chaining multi-step pattern match operations with unified error handling
  • Concurrency: spawn, send/receive message passing, Task.async/await, Agent state management, spawn_link/monitor
  • GenServer: start_link, init, handle_call (sync), handle_cast (async), handle_info, and Supervisor with restart strategies
  • Pipe operator |> for left-to-right function composition and & capture operator for anonymous function shorthand
  • Stream module for lazy evaluation — filter/map large collections without materializing intermediate lists
  • Macros: defmacro, quote/unquote for AST manipulation, use hooks, and @module attributes for compile-time metadata

Frequently Asked Questions

How does pattern matching with = work in Elixir?

In Elixir, = is a match operator, not an assignment operator. The runtime attempts to match the right-hand side value against the left-hand pattern, binding any unbound variables in the pattern. For example, {status, value} = {:ok, 42} binds status to :ok and value to 42. If the match fails (e.g., {:error, reason} = {:ok, 42}), a MatchError is raised. This applies everywhere: function arguments, case clauses, and the with expression.

What is the pin operator (^) and when do I use it?

The pin operator ^ prevents a variable from being rebound during pattern matching and instead forces equality checking against its current value. Without ^, x = 2 in a match would silently rebind x. With ^x = 2, Elixir checks that x already equals 2 and raises a MatchError if not. Use it in case clauses or receive blocks when you want to match against an existing variable's value rather than capture a new one.

What is the difference between handle_call and handle_cast in GenServer?

handle_call/3 handles synchronous messages sent with GenServer.call/2 — the caller blocks until the server responds with {:reply, response, new_state}. handle_cast/2 handles asynchronous messages sent with GenServer.cast/2 — the caller returns immediately (fire-and-forget) and the server returns {:noreply, new_state} without sending a response. Use call for queries that need a return value and cast for commands where you do not need confirmation.

What is the with expression and when should I use it?

The with expression chains multiple <- pattern matches sequentially. If all matches succeed, the do block executes. If any match fails, the else block handles the non-matching value. This is the idiomatic Elixir pattern for multi-step operations where each step can return {:ok, value} or {:error, reason}, replacing deeply nested case expressions. Use with when you have two or more operations that must all succeed before proceeding.

What is the difference between Enum and Stream?

Enum functions are eager — they process the entire collection immediately and return a new list. Stream functions are lazy — they build a composable transformation pipeline that only executes when consumed by Enum.to_list, Enum.take, etc. For large collections or infinite ranges (1..1_000_000), Stream avoids creating large intermediate lists. Use Enum for small-to-medium collections and Stream when processing large datasets or composing many transformation steps.

How do Elixir processes differ from OS threads?

Elixir processes (via the BEAM VM) are extremely lightweight — you can spawn millions of them. Each process has its own isolated heap (no shared memory), communicates only via message passing, and is scheduled cooperatively by the BEAM scheduler across OS threads. Process isolation means a crash in one process does not affect others unless they are linked. This model enables fault-tolerant supervision trees where crashed processes are automatically restarted.

What does use GenServer do inside a module?

use GenServer calls the GenServer.__using__/1 macro at compile time, which injects default implementations of all GenServer callbacks (init, handle_call, handle_cast, handle_info, terminate, code_change) into the module. You then override only the callbacks you need. This is Elixir's macro-based mixin pattern — use triggers __using__ in the target module, which can inject any code the library author defines.

How does the pipe operator |> work?

The pipe operator |> takes the result of the expression on its left and passes it as the first argument to the function on its right. This enables a readable, top-to-bottom composition style without nested function calls. "Hello World" |> String.downcase() |> String.split() is equivalent to String.split(String.downcase("Hello World")). The pipe operator is purely syntactic sugar — it has no runtime overhead.