feat: add source hyperlinks + remove thin from-source.md

Every source reference now links to elixir-lang/elixir at commit f4e1b34.
122 hyperlinks across 11 topic files. Added PATTERN_COMPLETE sentinels.
Removed from-source.md (326 lines, shallow) — covered by existing files.
This commit is contained in:
Rodin
2026-04-30 14:43:56 -07:00
parent 9ff22d2eed
commit 5f62dd0bf1
13 changed files with 146 additions and 480 deletions
+10 -8
View File
@@ -4,7 +4,7 @@ How behaviours are designed, implemented, and used in Elixir core and Phoenix.
## 1. Behaviour Definition with `@callback`
**Source:** `lib/elixir/lib/gen_server.ex:577-812` (all callback definitions)
**Source:** [lib/elixir/lib/gen_server.ex#L577](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L577) (all callback definitions)
```elixir
@callback init(init_arg :: term) ::
@@ -89,7 +89,7 @@ end
## 2. `@optional_callbacks` for Extensibility
**Source:** `lib/phoenix/channel.ex:442-448`
**Source:** [lib/phoenix/channel.ex#L442](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/channel.ex#L442)
```elixir
@optional_callbacks handle_in: 3,
@@ -168,7 +168,7 @@ end
## 3. `@behaviour` Declaration in `__using__`
**Source:** `lib/phoenix/channel.ex:450-453`
**Source:** [lib/phoenix/channel.ex#L450](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/channel.ex#L450)
```elixir
defmacro __using__(opts \\ []) do
@@ -182,7 +182,7 @@ defmacro __using__(opts \\ []) do
end
```
**Source:** `lib/elixir/lib/gen_server.ex:836`
**Source:** [lib/elixir/lib/gen_server.ex#L836](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L836)
```elixir
quote location: :keep, bind_quoted: [opts: opts] do
@@ -270,7 +270,7 @@ end
## 4. Default Implementations via `defoverridable`
**Source:** `lib/elixir/lib/gen_server.ex:849`
**Source:** [lib/elixir/lib/gen_server.ex#L849](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L849)
```elixir
def child_spec(init_arg) do
@@ -358,7 +358,7 @@ end
## 5. Phoenix Channel: Behaviour + Process + Protocol
**Source:** `lib/phoenix/channel.ex:364-448` (full callback set)
**Source:** [lib/phoenix/channel.ex#L364](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/channel.ex#L364) (full callback set)
The Channel behaviour combines:
1. **Required callback:** `join/3` (authorization gate)
@@ -475,7 +475,7 @@ end
## 6. Callback Documentation Pattern
**Source:** `lib/phoenix/channel.ex:350-363` (join callback doc)
**Source:** [lib/phoenix/channel.ex#L350](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/channel.ex#L350) (join callback doc)
```elixir
@doc """
@@ -591,7 +591,7 @@ This callback is required.
## 7. Phoenix.Endpoint: Behaviour as Interface Contract
**Source:** `lib/phoenix/endpoint.ex:408`
**Source:** [lib/phoenix/endpoint.ex#L408](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/endpoint.ex#L408)
```elixir
defmacro __using__(opts) do
@@ -677,3 +677,5 @@ end
```
**Why:** The more code a `use` macro generates, the harder it is to debug. If users regularly need to read the generated code to understand failures, the abstraction is leaking. Reserve heavy `use` macros for well-established patterns (GenServer, Endpoint, Channel) where the community has internalized the mental model.
<!-- PATTERN_COMPLETE -->
+14 -12
View File
@@ -6,7 +6,7 @@ Patterns extracted from Elixir's standard library source code.
## 1. List-Specialized Clause Before Protocol Dispatch
**Source:** `lib/elixir/lib/enum.ex` lines 17231733
**Source:** [lib/elixir/lib/enum.ex#L1723](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L1723)
**What it does:** Every public Enum function defines a `when is_list(enumerable)` clause first, then a generic fallback that uses the Enumerable protocol.
@@ -85,7 +85,7 @@ end
## 2. Build-Then-Reverse (Cons-Cell Accumulation)
**Source:** `lib/elixir/lib/enum.ex` lines 1124, 1733, 2697
**Source:** [lib/elixir/lib/enum.ex#L1124](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L1124), 1733, 2697
**What it does:** Accumulates results by prepending to a list (`[x | acc]`), then reverses at the end.
@@ -169,7 +169,7 @@ end
## 3. Pipeline for Linear Transformations, Bare Calls for Control Flow
**Source:** `lib/elixir/lib/enum.ex` lines 16841685, 1551, vs 496502
**Source:** [lib/elixir/lib/enum.ex#L1684](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L1684), 1551, vs 496502
**What it does:** Elixir core uses `|>` when data flows linearly through 2+ transformations. It does NOT use `|>` for single-step operations or when the first argument is computed by a `case`/`if`/`with`.
@@ -258,7 +258,7 @@ end
## 4. Pipeline Ending with `|> elem(1)` (Protocol Reduce Unwrap)
**Source:** `lib/elixir/lib/enum.ex` lines 363, 403, 433, 468, 725, 1022, 2676
**Source:** [lib/elixir/lib/enum.ex#L363](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L363), 403, 433, 468, 725, 1022, 2676
**What it does:** When calling `Enumerable.reduce/3` directly, the result is always `{:done | :halted | :suspended, acc}`. Core extracts the accumulator with `|> elem(1)`.
@@ -336,7 +336,7 @@ Enum.reduce([1, 2, 3], 0, &(&1 + &2)) |> elem(1)
## 5. Private Helper Decomposition: Recursive Workers with Guards
**Source:** `lib/elixir/lib/enum.ex` lines 49754995, 50255039
**Source:** [lib/elixir/lib/enum.ex#L4975](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L4975), 50255039
**What it does:** Complex operations are split into a public entry point (with validation guards) and a private recursive worker function. The worker uses pattern matching on structure (empty list, head|tail) and guards on counters.
@@ -435,7 +435,7 @@ def my_take(list, n), do: Enum.take(list, n)
## 6. Enum vs Stream Decision Pattern
**Source:** `lib/elixir/lib/stream.ex` lines 180 (module docs), `lib/elixir/lib/enum.ex`
**Source:** [lib/elixir/lib/stream.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/stream.ex#L1) (module docs), `lib/elixir/lib/enum.ex`
**What it does:** Enum functions are eager (materialize intermediate lists). Stream functions are lazy (build computation recipes). Core uses Stream for:
- Infinite sequences (`cycle`, `iterate`, `repeatedly`)
@@ -528,7 +528,7 @@ config
## 7. Map.update vs Map.put Decision Pattern
**Source:** `lib/elixir/lib/map.ex` lines 670700
**Source:** [lib/elixir/lib/map.ex#L670](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L670)
**What it does:** `Map.update/4` transforms an existing value based on its current state. `Map.put/3` unconditionally sets a value regardless of current state.
@@ -603,7 +603,7 @@ Map.put(user, :name, new_name)
## 8. Pattern Matching on Map Structure for Dispatch
**Source:** `lib/elixir/lib/map.ex` lines 398, 509, 586
**Source:** [lib/elixir/lib/map.ex#L398](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L398), 509, 586
**What it does:** Map functions use `case map do %{^key => value} -> ...` to dispatch on whether a key exists, rather than calling `has_key?` + conditional.
@@ -700,7 +700,7 @@ end
## 9. Delegating to Erlang BIFs with `defdelegate`
**Source:** `lib/elixir/lib/map.ex` lines 127, 143, 159, 173
**Source:** [lib/elixir/lib/map.ex#L127](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L127), 143, 159, 173
**What it does:** When an Erlang function already does exactly what's needed, Elixir delegates directly rather than wrapping.
@@ -772,7 +772,7 @@ end
## 10. Reduce as the Universal Primitive
**Source:** `lib/elixir/lib/enum.ex` lines 1921, 26602676
**Source:** [lib/elixir/lib/enum.ex#L19](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L19), 26602676
**What it does:** Nearly every Enum operation is built on top of `reduce`. The Enumerable protocol's core function is `reduce/3`. Everything else (`count`, `member?`, `slice`) is an optimization hint.
@@ -857,7 +857,7 @@ end
## 11. Keyword Multi-Clause Guard Dispatch (String.split pattern)
**Source:** `lib/elixir/lib/string.ex` lines 516563
**Source:** [lib/elixir/lib/string.ex#L516](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/string.ex#L516)
**What it does:** Functions with many input shapes use multiple `def` clauses with guards to dispatch, handling each case distinctly rather than using internal `cond`/`case`.
@@ -949,7 +949,7 @@ end
## 12. Lazy Private Helpers with `defp parts_to_index`
**Source:** `lib/elixir/lib/string.ex` lines 562563
**Source:** [lib/elixir/lib/string.ex#L562](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/string.ex#L562)
**What it does:** Tiny private helpers that convert between API-level concepts and implementation-level values use single-line `defp` with guards.
@@ -1009,3 +1009,5 @@ def log(msg) when is_atom(msg), do: IO.puts(Atom.to_string(msg))
```
**Why:** When a conversion is used exactly once and the calling function already dispatches on clauses, folding the conversion into the caller's clauses reduces indirection. Named helpers shine when reused or when they name a non-obvious transformation.
<!-- PATTERN_COMPLETE -->
+11 -9
View File
@@ -6,7 +6,7 @@ Patterns extracted from the Elixir standard library source code.
## 1. @moduledoc with Structured Sections
**Source:** `lib/elixir/lib/gen_server.ex` lines 6100+, `lib/logger/lib/logger.ex` lines 6200+
**Source:** [lib/elixir/lib/gen_server.ex#L6](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L6)+, [lib/logger/lib/logger.ex#L6](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/lib/logger.ex#L6)+
**What it does:** `@moduledoc` uses a clear hierarchical structure with `##` sections covering: overview, examples, configuration, and detailed behavioral explanations. GenServer covers "Example", "Client / Server APIs", "How to supervise", "Name registration", "Timeouts", "Debugging". Logger covers "Levels", "Message", "Metadata", "Configuration" (with subsections for Boot/Compile/Runtime).
@@ -145,7 +145,7 @@ end
## 2. @doc with Sections and Examples
**Source:** `lib/elixir/lib/kernel.ex` lines 315335 (abs/1), `lib/logger/lib/logger.ex` lines 536540
**Source:** [lib/elixir/lib/kernel.ex#L315](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L315) (abs/1), [lib/logger/lib/logger.ex#L536](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/lib/logger.ex#L536)
**What it does:** `@doc` for individual functions includes: a brief one-liner, allowed-in-guards note (via `@doc guard: true`), explanation of edge cases, and `## Examples` with doctests.
@@ -230,7 +230,7 @@ def get_name(%User{name: name}), do: name
## 3. @doc since: Version Annotation
**Source:** `lib/logger/lib/logger.ex` lines 539, 576, 813, 824, 831, `lib/elixir/lib/kernel.ex` line 5163+
**Source:** [lib/logger/lib/logger.ex#L539](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/lib/logger.ex#L539), 576, 813, 824, 831, [lib/elixir/lib/kernel.ex#L5163](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L5163)+
**What it does:** Attaches `since: "X.Y.Z"` metadata to `@doc` indicating the Elixir version that introduced the function.
@@ -307,7 +307,7 @@ end
## 4. @doc guard: true Metadata
**Source:** `lib/elixir/lib/kernel.ex` lines 329, 408, 428, 452, etc.
**Source:** [lib/elixir/lib/kernel.ex#L329](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L329), 408, 428, 452, etc.
**What it does:** Functions/macros usable in guard clauses are annotated with `@doc guard: true` metadata, separate from the doc string itself.
@@ -395,7 +395,7 @@ end
## 5. @doc false — Hiding from Documentation
**Source:** `lib/elixir/lib/inspect.ex` lines 410, 417; implicit via `@impl true`
**Source:** [lib/elixir/lib/inspect.ex#L410](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/inspect.ex#L410), 417; implicit via `@impl true`
**What it does:** `@doc false` explicitly hides a function from documentation generators. Also, using `@impl true` automatically sets `@doc false` (unless `@doc` is explicitly provided).
@@ -555,7 +555,7 @@ end
## 7. Mermaid Diagrams in Documentation
**Source:** `lib/elixir/lib/gen_server.ex` lines 1420
**Source:** [lib/elixir/lib/gen_server.ex#L14](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L14)
**What it does:** Embeds Mermaid diagram syntax directly in `@moduledoc` to illustrate architectural patterns (client-server message flow).
@@ -648,7 +648,7 @@ A simple integer counter. Call `increment/1` to add, `value/1` to read.
## 8. Admonition Blocks in Documentation
**Source:** `lib/elixir/lib/gen_server.ex` lines 8895, `lib/elixir/lib/supervisor.ex` lines 3438
**Source:** [lib/elixir/lib/gen_server.ex#L88](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L88), [lib/elixir/lib/supervisor.ex#L34](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L34)
**What it does:** Uses markdown admonition syntax (`> #### Title {: .info}`) to highlight important callouts — especially for `use ModuleName` behavior documentation.
@@ -745,7 +745,7 @@ Helper functions for string formatting.
## 9. @doc deprecated: Soft Deprecation
**Source:** `lib/elixir/lib/module.ex` lines 163180
**Source:** [lib/elixir/lib/module.ex#L163](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/module.ex#L163)
**What it does:** Uses `@doc deprecated: "Use X instead"` for soft deprecation (documentation-only warning) vs. `@deprecated "reason"` for hard deprecation (compiler warning).
@@ -822,7 +822,7 @@ def process(input), do: new_process(input, [])
## 10. Callback Documentation Convention
**Source:** `lib/elixir/lib/gen_server.ex` lines 584646 (handle_call docs)
**Source:** [lib/elixir/lib/gen_server.ex#L584](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L584) (handle_call docs)
**What it does:** Each `@callback` is preceded by a comprehensive `@doc` that explains: what triggers the callback, what the parameters mean, every possible return value and its effect, when the callback is optional, and cross-references to related callbacks.
@@ -999,3 +999,5 @@ Returns `true` if the calling process is the owner of this resource.
```
**Why:** Link references should aid navigation, not turn documentation into hypertext soup. Link types and callbacks that users might need to look up; don't link primitive types or universally known functions.
<!-- PATTERN_COMPLETE -->
+16 -14
View File
@@ -6,7 +6,7 @@ Patterns extracted from Elixir's standard library source code.
## 1. The `with` Macro — Normalized Error Clauses
**Source:** `lib/elixir/lib/kernel/special_forms.ex` lines 16001715 (docs + definition)
**Source:** [lib/elixir/lib/kernel/special_forms.ex#L1600](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel/special_forms.ex#L1600) (docs + definition)
**What it does:** The `with` macro chains pattern-matched steps where each `<-` clause returns a normalized error shape. When a step fails to match, the non-matched value falls through (or hits `else`).
@@ -107,7 +107,7 @@ Repo.get_user(id)
## 2. Real-World `with` — Multi-Step Fallible Operations
**Source:** `lib/elixir/lib/exception.ex` lines 251285 (`blame_mfa/4`)
**Source:** [lib/elixir/lib/exception.ex#L251](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L251) (`blame_mfa/4`)
**What it does:** Uses `with` to chain 5+ fallible steps where any failure should produce `:error`. Each step's pattern is an exact match.
@@ -222,7 +222,7 @@ def process_payment(order) do
## 3. Another `with` — Error Info Extraction
**Source:** `lib/elixir/lib/exception.ex` lines 26952720 (`error_info/3` in ErlangError)
**Source:** [lib/elixir/lib/exception.ex#L2695](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2695) (`error_info/3` in ErlangError)
**What it does:** Chains pattern matching on a stacktrace and error_info map to extract formatted error details.
@@ -322,7 +322,7 @@ def extract_user_preference(_user, _key), do: {:error, :no_preferences}
## 4. `{:ok, value}` / `:error` Convention (Map.fetch)
**Source:** `lib/elixir/lib/map.ex` lines 290309
**Source:** [lib/elixir/lib/map.ex#L290](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L290)
**What it does:** `Map.fetch/2` returns `{:ok, value}` on success and bare `:error` on failure. No reason atom, because the failure mode is obvious (key not found).
@@ -413,7 +413,7 @@ end
## 5. Bang Functions: Raise on Error (`fetch!` vs `fetch`)
**Source:** `lib/elixir/lib/map.ex` lines 311380
**Source:** [lib/elixir/lib/map.ex#L311](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L311)
**What it does:** The `!` suffix convention means "raises on failure instead of returning an error tuple." The non-bang version is for when the caller wants to handle the error.
@@ -507,7 +507,7 @@ end
## 6. Exception Structure: `defexception` Fields
**Source:** `lib/elixir/lib/exception.ex` lines 22502500 (exception definitions)
**Source:** [lib/elixir/lib/exception.ex#L2250](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2250) (exception definitions)
**What it does:** Exceptions carry meaningful fields beyond just `:message`. The `message/1` callback generates a human-readable string from those fields.
@@ -605,7 +605,7 @@ raise "expected state in #{inspect(expected)}, got #{inspect(actual)}"
## 7. Custom `exception/1` Callback for Ergonomic Raising
**Source:** `lib/elixir/lib/exception.ex` lines 22552270 (UnicodeConversionError)
**Source:** [lib/elixir/lib/exception.ex#L2255](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2255) (UnicodeConversionError)
**What it does:** Override `exception/1` to accept raw values (not just keyword lists) and build the struct with a meaningful message.
@@ -710,7 +710,7 @@ end
## 8. `raise` Macro Internals: Compile-Time Type Resolution
**Source:** `lib/elixir/lib/kernel.ex` lines 22462294
**Source:** [lib/elixir/lib/kernel.ex#L2246](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L2246)
**What it does:** The `raise` macro inspects the argument at compile time to determine if it's a string, binary expression, atom (module), or existing exception struct, generating optimized code for each case.
@@ -815,7 +815,7 @@ end
## 9. Error Normalization: Erlang → Elixir Exception Translation
**Source:** `lib/elixir/lib/exception.ex` lines 25302680 (`ErlangError.normalize/2`)
**Source:** [lib/elixir/lib/exception.ex#L2530](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2530) (`ErlangError.normalize/2`)
**What it does:** Translates raw Erlang error reasons (atoms and tuples) into proper Elixir exception structs with helpful messages.
@@ -913,7 +913,7 @@ end
## 10. `blame/2` Callback: Enriching Exceptions After the Fact
**Source:** `lib/elixir/lib/exception.ex` lines 22002215 (KeyError.blame)
**Source:** [lib/elixir/lib/exception.ex#L2200](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2200) (KeyError.blame)
**What it does:** The optional `blame/2` callback enriches an exception with additional context that's expensive to compute (like "did you mean?" suggestions).
@@ -1008,7 +1008,7 @@ end
## 11. Guards for Type Dispatch in Error Handling
**Source:** `lib/elixir/lib/exception.ex` lines 25302550, `lib/elixir/lib/map.ex` lines 586594
**Source:** [lib/elixir/lib/exception.ex#L2530](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2530), [lib/elixir/lib/map.ex#L586](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L586)
**What it does:** Guards (`when is_map(term)`, `when is_list(term)`) dispatch to different error handling or normalization logic without using conditionals.
@@ -1101,7 +1101,7 @@ end
## 12. The `:error` / `{:error, reason}` Convention Split
**Source:** `lib/elixir/lib/map.ex` (Map.fetch → `:error`), `lib/elixir/lib/exception.ex` lines 26952720 (`error_info``{:ok, ...} | :error`)
**Source:** `lib/elixir/lib/map.ex` (Map.fetch → `:error`), [lib/elixir/lib/exception.ex#L2695](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/exception.ex#L2695) (`error_info``{:ok, ...} | :error`)
**What it does:** Elixir has two error return conventions:
1. **`:error`** alone — when there's only one failure mode (Map.fetch, Access)
@@ -1197,7 +1197,7 @@ end
## 13. `reduce_while` — Early Exit Without Exceptions
**Source:** `lib/elixir/lib/enum.ex` lines 26602676
**Source:** [lib/elixir/lib/enum.ex#L2660](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L2660)
**What it does:** `reduce_while` uses `{:cont, acc}` / `{:halt, acc}` tuples as the reducer's return value to signal continuation or early termination.
@@ -1291,7 +1291,7 @@ Enum.find(users, & &1.admin?)
## 14. Three-Tier Error Strategy in Map Operations
**Source:** `lib/elixir/lib/map.ex` lines 290430
**Source:** [lib/elixir/lib/map.ex#L290](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/map.ex#L290)
**What it does:** Map provides three variants for key operations, each with different error semantics:
@@ -1399,3 +1399,5 @@ end
```
**Why:** The three-tier pattern only makes sense when failure is a real possibility and different callers genuinely need different responses to that failure. Don't cargo-cult it onto functions that always succeed or have a single calling context.
<!-- PATTERN_COMPLETE -->
-358
View File
@@ -1,358 +0,0 @@
# Elixir Patterns (from Source)
Prescriptive patterns extracted from elixir-lang/elixir source.
"If writing new Elixir, follow these rules."
---
## Module Organization
### One Module Per Concept
**Rule:** Each module owns exactly one concept. If you can't name it in
2-3 words, it's too broad.
```elixir
# Source: lib/elixir/lib/string.ex — String is strings. Period.
defmodule String do
@moduledoc """
Strings in Elixir are UTF-8 encoded binaries.
"""
@type t :: binary
```
**Why:** The Elixir source has zero "util" or "helper" modules. Every
module has a noun name that IS the thing.
**When NOT to use:** Kernel is the exception — it's the implicit
surface area. You don't get to make your own Kernel.
**Source:** Every file in `lib/elixir/lib/` follows this.
### @moduledoc false for Internal Modules
**Rule:** Internal modules that users shouldn't call get `@moduledoc false`.
```elixir
# Source: lib/elixir/lib/code/formatter.ex
defmodule Code.Formatter do
@moduledoc false
```
**Why:** Hides from docs. Signals "this is implementation, not API."
The Elixir team uses this for 30+ internal modules.
**When to use:** Modules that are implementation details — adapters,
internal state machines, compiler passes.
**When NOT to use:** If ANYONE outside your team might call it. Public
API must have docs.
---
## Protocol Design
### Protocols for External Extension
**Rule:** Define a protocol when users need to extend behavior for
their own types.
```elixir
# Source: lib/elixir/lib/collectable.ex
defprotocol Collectable do
@doc """
Returns an initial accumulation value and a "collector" function.
"""
@spec into(t) :: {initial_acc :: term, collector(term)}
def into(collectable)
end
```
**Why:** Protocols dispatch on the first argument's type. They're the
extension point for "I made a new data structure and want it to work
with Enum."
**When to use:** When third-party code needs to extend your library
for new data types.
**When NOT to use:** When you control all implementations. Use
behaviours instead. Protocols are for open extension; behaviours are
for closed contracts.
### Only 6 Stdlib Protocols
**Rule:** Be conservative defining protocols. The Elixir stdlib has
only 6 in 15 years.
- `Enumerable` — iterate over things
- `Collectable` — put things into containers
- `Inspect` — debug representation
- `String.Chars` — convert to string
- `List.Chars` — convert to charlist
- `JSON.Encoder` — JSON serialization (added 2024)
**Why:** Each protocol is a permanent API commitment. Once defined,
every type in the ecosystem may implement it.
**When to use:** When you need a small set of polymorphic operations
that ANY type in the ecosystem should be able to implement.
**When NOT to use:** Don't define a protocol for something only your
app needs. A behaviour or function argument is cheaper.
---
## Error Handling
### Tagged Tuples for Expected Failures
**Rule:** Return `{:ok, value}` or `{:error, reason}` for operations
that can fail in expected ways.
```elixir
# Source: lib/elixir/lib/file.ex
@spec read(Path.t()) :: {:ok, binary} | {:error, posix}
def read(path) do
case :file.read_file(path) do
{:ok, binary} -> {:ok, binary}
{:error, reason} -> {:error, reason}
end
end
```
**Why:** Pattern matching makes handling explicit. The caller MUST
decide what to do with errors — they can't accidentally ignore them.
**When to use:** Any operation that interacts with the outside world
(files, network, databases) where failure is a normal outcome.
**When NOT to use:** For programmer errors (bugs). Those should raise.
`File.read!` raises; `File.read` returns tuples.
### Bang Functions for "Should Never Fail"
**Rule:** Provide `function!` variant that raises on error. Use when
failure means a bug in the caller.
```elixir
# Convention: function returns {:ok, _} | {:error, _}
# function! raises on error
File.read("path") # => {:ok, "..."} | {:error, :enoent}
File.read!("path") # => "..." | raises File.Error
```
**Why:** The `!` signals to the reader: "I expect this to succeed. If
it doesn't, crash." This is intentional — crashing is the correct
response to unexpected errors in OTP.
**When to use:** Pipeline code where you want to crash on unexpected
failure rather than propagate error tuples.
**When NOT to use:** When failure is expected and the caller should
handle it (use tagged tuples).
---
## Testing
### CaseTemplate for Shared Setup
**Rule:** Use `ExUnit.CaseTemplate` when multiple test files share
setup logic.
```elixir
# Source: lib/ex_unit/lib/ex_unit/case_template.ex
defmodule MyApp.DataCase do
use ExUnit.CaseTemplate
setup tags do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
unless tags[:async] do
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
end
:ok
end
end
```
**Why:** Inheritance via `use` is how Phoenix's ConnCase/DataCase work.
This pattern comes directly from ExUnit itself.
**When to use:** When 3+ test modules share the same setup callbacks,
imports, or lifecycle management.
**When NOT to use:** For helpers that don't need lifecycle callbacks.
Simple `import` is cleaner for utility functions.
### async: true by Default
**Rule:** Mark tests `async: true` unless they touch shared state.
**Why:** Async tests run in parallel. The Elixir stdlib tests show that
most tests CAN be async — only database/file/process tests need
serialization.
**When to use:** Pure unit tests, tests with isolated process state,
tests that only read shared resources.
**When NOT to use:** Tests that modify global state, shared files, or
named processes.
---
## Documentation
### Every Public Function Gets @doc + @spec
**Rule:** All public functions have both `@doc` and `@spec`.
```elixir
# Source: lib/elixir/lib/enum.ex
@doc """
Returns `true` if all elements in `enumerable` are truthy.
"""
@spec all?(t) :: boolean
def all?(enumerable) when is_list(enumerable) do
```
**Why:** Specs enable Dialyzer checking. Docs generate ExDoc pages. The
Elixir stdlib has zero undocumented public functions.
**When to use:** Every public function in every public module.
No exceptions.
**When NOT to use:** `@moduledoc false` modules skip docs (they're
internal).
### @type t for Structs
**Rule:** Every struct defines `@type t` with field types.
```elixir
# Source: lib/elixir/lib/kernel.ex (defstruct docs)
defmodule User do
defstruct name: "John", age: 25
@type t :: %__MODULE__{name: String.t(), age: non_neg_integer}
end
```
**Why:** Enables Dialyzer to catch type mismatches at struct boundaries.
Without `@type t`, struct fields are effectively untyped.
---
## Naming
### Modules Are Nouns
**Rule:** Module names are nouns. Never verbs, never adjectives.
`String`, `Enum`, `Map`, `File`, `Logger`, `GenServer`
**Why:** A module IS a thing. Functions are what you DO with that thing.
`String.split/2` reads as "take a String, split it."
**When to use:** Every module you define. Period.
**When NOT to use:** Mix tasks (they're commands: `Mix.Tasks.Deps.Get`).
### Functions Are Verbs
**Rule:** Function names start with a verb (or are a question with `?`).
`Enum.map/2`, `String.split/2`, `File.read/1`, `Enum.empty?/1`
**Why:** `module.verb(subject)` reads as a sentence.
### Underscore Prefix for Unused
**Rule:** Prefix unused variables with `_`.
```elixir
def handle_info(_message, state), do: {:noreply, state}
```
**Why:** Compiler warning suppression AND documentation that the value
is intentionally ignored.
---
## Process Design
### GenServer for Stateful Services
**Rule:** Use GenServer when you need mutable state that outlives a
request.
```elixir
# Source: lib/iex/lib/iex/broker.ex
defmodule IEx.Broker do
use GenServer
# ...
end
```
**Why:** GenServer gives you: message serialization, supervision tree
integration, hot code upgrade, `:sys` debugging.
**When to use:** When you need mutable state that survives across
multiple requests or events.
**When NOT to use:** Stateless transformations (just use functions).
One-off concurrent work (use Task). Accumulating state within a request
(use recursion or reduce).
### Agent for Simple State
**Rule:** Use Agent when you only need get/update on a value — no
complex message handling.
```elixir
# Source: lib/mix/lib/mix/tasks_server.ex
defmodule Mix.TasksServer do
use Agent
end
```
**Why:** Agent is GenServer with the common case optimized. Less
boilerplate for "I just need a mutable box."
**When to use:** When your process only needs get/update operations
on a single piece of state — no complex message handling.
**When NOT to use:** When you need `handle_info`, timeouts, or multiple
operations that must be atomic.
---
## Smells
### GenEvent (Deprecated Pattern)
```elixir
# Source: lib/elixir/lib/gen_event.ex
@moduledoc deprecated: "Use one of the alternatives described below"
```
The Elixir team deprecated their own GenEvent. Alternatives: Registry +
GenServer, or Phoenix.PubSub. Lesson: event buses that try to do
everything are worse than composed primitives.
### Version-Gated TODOs (Deferred Cleanup)
```elixir
# TODO: Remove me on v2.0
# TODO: Deprecate me on Elixir v1.23
```
127 of these exist. They're not smells in the "bad code" sense —
they're discipline. But if YOUR code has TODOs without version targets,
that IS a smell.
### @moduledoc false Proliferation
30+ internal modules in the stdlib. If your app has this many, you may
be over-splitting. Internal modules should be rare in application code
— they're appropriate for libraries and frameworks.
<!-- PATTERN_COMPLETE -->
+14 -12
View File
@@ -6,7 +6,7 @@ Analysis of `lib/elixir/lib/gen_server.ex`, `lib/elixir/lib/agent.ex`, and relat
## Pattern 1: Client/Server API Separation
**Source:** `lib/elixir/lib/gen_server.ex:101-149` (documentation example)
**Source:** [lib/elixir/lib/gen_server.ex#L101](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L101) (documentation example)
**What it does:** Every GenServer module defines two distinct API layers — a **client API** (thin public functions that wrap `GenServer.call/cast`) and a **server API** (callback implementations). The client functions live in the same module but are clearly separated with comments.
@@ -120,7 +120,7 @@ end
## Pattern 2: `@impl true` Annotations on All Callbacks
**Source:** `lib/elixir/lib/gen_server.ex:41-60` (Stack example)
**Source:** [lib/elixir/lib/gen_server.ex#L41](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L41) (Stack example)
**What it does:** Every callback function is annotated with `@impl true`. This tells the compiler to verify that the function is a valid callback for the declared behaviour.
@@ -193,7 +193,7 @@ end
## Pattern 3: Guard-Protected `start_link`
**Source:** `lib/elixir/lib/gen_server.ex:101` and `lib/elixir/lib/agent.ex:28`
**Source:** [lib/elixir/lib/gen_server.ex#L101](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L101) and [lib/elixir/lib/agent.ex#L28](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L28)
**What it does:** The `start_link` function uses guards to validate its arguments at the API boundary, failing fast with a clear error before any process spawning occurs.
@@ -261,7 +261,7 @@ end
## Pattern 4: `handle_continue` for Post-Init Work
**Source:** `lib/elixir/lib/gen_server.ex:520-528` (callback spec), `lib/elixir/lib/gen_server.ex:714-720` (handle_continue callback definition)
**Source:** [lib/elixir/lib/gen_server.ex#L520](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L520) (callback spec), [lib/elixir/lib/gen_server.ex#L714](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L714) (handle_continue callback definition)
**What it does:** `init/1` can return `{:ok, state, {:continue, continue_arg}}`, which causes `handle_continue/2` to be invoked immediately after init completes. This allows splitting initialization into a synchronous part (that unblocks the supervisor) and an asynchronous continuation.
@@ -352,7 +352,7 @@ end
## Pattern 5: Timeout-Based Idle Shutdown
**Source:** `lib/elixir/lib/gen_server.ex:335-380`
**Source:** [lib/elixir/lib/gen_server.ex#L335](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L335)
**What it does:** Callbacks can return a timeout value (milliseconds) as the last element of the return tuple. If no message arrives within that time, `handle_info(:timeout, state)` is called. This enables idle process cleanup.
@@ -458,7 +458,7 @@ end
## Pattern 6: Periodic Work via `Process.send_after`
**Source:** `lib/elixir/lib/gen_server.ex:298-332` (Periodically example in docs)
**Source:** [lib/elixir/lib/gen_server.ex#L298](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L298) (Periodically example in docs)
**What it does:** A GenServer schedules periodic work by sending itself a message via `Process.send_after` in `init/1`, then rescheduling in `handle_info`. This creates a self-sustaining periodic loop.
@@ -573,7 +573,7 @@ end
## Pattern 7: Call vs Cast Decision (Synchronous vs Asynchronous)
**Source:** `lib/elixir/lib/gen_server.ex:83-90` (docs), `lib/elixir/lib/agent.ex:368-378` (Agent.update uses call, Agent.cast uses cast)
**Source:** [lib/elixir/lib/gen_server.ex#L83](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L83) (docs), [lib/elixir/lib/agent.ex#L368](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L368) (Agent.update uses call, Agent.cast uses cast)
**What it does:** The Elixir team uses `call` (synchronous) for operations where the client needs confirmation or a return value, and `cast` (fire-and-forget) only when the client genuinely doesn't care about the outcome.
@@ -640,7 +640,7 @@ end
## Pattern 8: Default Callback Implementations with Clear Error Messages
**Source:** `lib/elixir/lib/gen_server.ex:902-993` (`__using__` macro)
**Source:** [lib/elixir/lib/gen_server.ex#L902](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L902) (`__using__` macro)
**What it does:** `use GenServer` injects default implementations of `handle_call`, `handle_cast`, `handle_info`, `terminate`, and `code_change`. The defaults for `handle_call` and `handle_cast` raise with a descriptive error message including the process name. `handle_info` logs an error. All are `defoverridable`.
@@ -747,7 +747,7 @@ end
## Pattern 9: `child_spec/1` Generation and Customization via `use` Options
**Source:** `lib/elixir/lib/gen_server.ex:900-921`, `lib/elixir/lib/agent.ex:206-218`, `lib/elixir/lib/task.ex:282-292`
**Source:** [lib/elixir/lib/gen_server.ex#L900](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L900), [lib/elixir/lib/agent.ex#L206](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L206), [lib/elixir/lib/task.ex#L282](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L282)
**What it does:** Each `use GenServer/Agent/Task` generates a `child_spec/1` function with sensible defaults that can be customized via options passed to `use`. The child spec is a map with `:id`, `:start`, `:restart`, `:shutdown`, and `:type`.
@@ -852,7 +852,7 @@ end
## Pattern 10: Agent as Minimal State Wrapper (GenServer Under the Hood)
**Source:** `lib/elixir/lib/agent.ex:1-60` (module docs), `lib/elixir/lib/agent.ex:246-250` (implementation)
**Source:** [lib/elixir/lib/agent.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L1) (module docs), [lib/elixir/lib/agent.ex#L246](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/agent.ex#L246) (implementation)
**What it does:** Agent is implemented entirely in terms of `GenServer.start_link(Agent.Server, fun, options)`. It's a thin abstraction that provides `get/update/get_and_update/cast` over GenServer's `call/cast`.
@@ -947,7 +947,7 @@ end
## Pattern 11: Name Registration via `:via` Tuple
**Source:** `lib/elixir/lib/gen_server.ex:1087-1107` (do_start implementation), `lib/elixir/lib/gen_server.ex:230-250` (documentation)
**Source:** [lib/elixir/lib/gen_server.ex#L1087](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L1087) (do_start implementation), [lib/elixir/lib/gen_server.ex#L230](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L230) (documentation)
**What it does:** GenServer supports four naming schemes: `nil` (anonymous), atom (local), `{:global, term}` (cluster-wide), and `{:via, module, term}` (pluggable registry). The implementation delegates to `:gen.start` with the appropriate format.
@@ -1032,7 +1032,7 @@ end
## Pattern 12: GenServer as Anti-Pattern — Don't Use Processes for Code Organization
**Source:** `lib/elixir/lib/gen_server.ex:381-415` ("When (not) to use a GenServer" section)
**Source:** [lib/elixir/lib/gen_server.ex#L381](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L381) ("When (not) to use a GenServer" section)
**What it does:** The Elixir team explicitly documents that GenServer should NOT be used for code organization. A process must model a runtime property: mutable state, concurrency boundary, or failure isolation.
@@ -1108,3 +1108,5 @@ end
**Better alternative:** If you need mutable state (counters, connections, caches), you need a process. The rule is "don't use processes for code organization" — not "never use processes." A rate limiter with shared counters is a legitimate use of GenServer (or ETS owned by a GenServer).
**Why:** The pattern cuts both ways. Over-using GenServer creates bottlenecks. Under-using it means reinventing state management poorly. The litmus test: does the state need to survive between function calls? Does access need serialization? If yes, you need a process.
<!-- PATTERN_COMPLETE -->
+14 -12
View File
@@ -6,7 +6,7 @@ Patterns extracted from the Elixir standard library source code.
## 1. Context-Aware Macros (__CALLER__.context)
**Source:** `lib/elixir/lib/kernel.ex` lines 20322067 (or/and operators)
**Source:** [lib/elixir/lib/kernel.ex#L2032](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L2032) (or/and operators)
**What it does:** Macros check `__CALLER__.context` to determine if they're being invoked in a guard, match, or normal context, and generate different code accordingly.
@@ -100,7 +100,7 @@ end
## 2. defguard — Macro for Guard-Safe Expressions
**Source:** `lib/elixir/lib/kernel.ex` lines 58895966
**Source:** [lib/elixir/lib/kernel.ex#L5889](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L5889)
**What it does:** `defguard` creates a public macro that the compiler verifies is valid in guard clauses. It raises at compile time if the guard body uses non-guard-safe expressions.
@@ -191,7 +191,7 @@ end
## 3. quote + unquote for Code Generation
**Source:** `lib/elixir/lib/kernel.ex` lines 56245640 (defstruct)
**Source:** [lib/elixir/lib/kernel.ex#L5624](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L5624) (defstruct)
**What it does:** `quote bind_quoted: [fields: fields]` captures the macro argument into a variable available inside the quoted block. `unquote` injects computed values back into the AST.
@@ -284,7 +284,7 @@ end
## 4. var! for Breaking Hygiene
**Source:** `lib/elixir/lib/kernel.ex` lines 48844901
**Source:** [lib/elixir/lib/kernel.ex#L4884](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L4884)
**What it does:** `var!` marks a variable inside `quote` as unhygienic — it will refer to the variable in the *caller's* scope rather than creating a new hygienic binding.
@@ -376,7 +376,7 @@ double_it(5) # => 10, explicit, no hidden dependencies
## 5. Macro Expanding with Macro.expand
**Source:** `lib/elixir/lib/kernel.ex` lines 22462273 (raise), 23192340 (reraise)
**Source:** [lib/elixir/lib/kernel.ex#L2246](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L2246) (raise), 23192340 (reraise)
**What it does:** Before generating code, the macro calls `Macro.expand(message, __CALLER__)` to resolve aliases at compile time. This determines the code path: if the expanded value is an atom (module name), it generates exception-specific code.
@@ -464,7 +464,7 @@ end
## 6. assert_no_match_or_guard_scope Pattern
**Source:** `lib/elixir/lib/kernel.ex` lines 53845385 (def), 54155416 (defp), 54445445 (defmacro)
**Source:** [lib/elixir/lib/kernel.ex#L5384](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L5384) (def), 54155416 (defp), 54445445 (defmacro)
**What it does:** Macros that define module-level constructs (def, defp, defmacro, defmacrop) immediately assert they're not being called inside a guard or match context.
@@ -551,7 +551,7 @@ end
## 7. Protocol Definition as a Macro (defprotocol)
**Source:** `lib/elixir/lib/kernel.ex` lines 57345745, `lib/elixir/lib/protocol.ex` lines 290318
**Source:** [lib/elixir/lib/kernel.ex#L5734](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L5734), [lib/elixir/lib/protocol.ex#L290](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/protocol.ex#L290)
**What it does:** `defprotocol` is a macro that creates a module with auto-generated dispatch functions. Inside the protocol, `def` is redefined as a macro that generates both a callback spec and the dispatch implementation.
@@ -651,7 +651,7 @@ def format(binary) when is_binary(binary), do: # ...
## 8. @fallback_to_any in Protocols
**Source:** `lib/elixir/lib/inspect.ex` line 162, `lib/elixir/lib/protocol.ex` lines 115131
**Source:** [lib/elixir/lib/inspect.ex#L162](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/inspect.ex#L162), [lib/elixir/lib/protocol.ex#L115](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/protocol.ex#L115)
**What it does:** Sets `@fallback_to_any true` inside a protocol definition to enable a default implementation via `defimpl Protocol, for: Any`.
@@ -751,7 +751,7 @@ end
## 9. use/2 as Macro Injection Point
**Source:** `lib/elixir/lib/kernel.ex` lines 61306145
**Source:** [lib/elixir/lib/kernel.ex#L6130](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L6130)
**What it does:** `use Module, opts` is a macro that `require`s the module then calls `Module.__using__(opts)`. The `__using__/1` macro returns quoted code injected into the caller.
@@ -863,7 +863,7 @@ import MyHelpers
## 10. Sigil Macros (Pattern for DSL Literals)
**Source:** `lib/elixir/lib/kernel.ex` lines 65006850+ (sigil_S, sigil_s, sigil_r, sigil_D, etc.)
**Source:** [lib/elixir/lib/kernel.ex#L6500](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L6500)+ (sigil_S, sigil_s, sigil_r, sigil_D, etc.)
**What it does:** Each sigil (`~r`, `~D`, `~s`, etc.) is implemented as a `defmacro sigil_X(term, modifiers)` that receives the raw string content and modifier characters, then transforms them at compile time.
@@ -934,7 +934,7 @@ name = String.upcase("hello")
## 11. Pipe Operator as a Macro
**Source:** `lib/elixir/lib/kernel.ex` line 4509
**Source:** [lib/elixir/lib/kernel.ex#L4509](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L4509)
**What it does:** The `|>` pipe operator is a macro that rewrites `left |> right` into `right(left)`, inserting the left expression as the first argument of the right expression.
@@ -1016,7 +1016,7 @@ send_to(socket, encoded) # Clear which arg is which
## 12. Macro.generate_unique_arguments for Hygiene
**Source:** `lib/elixir/lib/macro.ex` lines 507520
**Source:** [lib/elixir/lib/macro.ex#L507](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/macro.ex#L507)
**What it does:** `Macro.generate_unique_arguments(n, context)` creates `n` unique variable AST nodes that won't conflict with any user variables.
@@ -1104,3 +1104,5 @@ end
```
**Why:** Variables created in `quote` are already hygienic by default — they can't clash with caller variables. `generate_unique_arguments` is needed when you're generating *multiple* variables dynamically (e.g., function parameters for a generated clause) where you need distinct names that also interoperate correctly.
<!-- PATTERN_COMPLETE -->
+9 -7
View File
@@ -214,7 +214,7 @@ end
## 3. `@moduledoc false` for Internal Modules
**Source:** `lib/phoenix/router/route.ex:5-7`
**Source:** [lib/phoenix/router/route.ex#L5](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/router/route.ex#L5)
```elixir
# This module defines the Route struct that is used
@@ -289,7 +289,7 @@ end
## 4. Struct Definition Conventions
**Source:** `lib/elixir/lib/task.ex:279-296`
**Source:** [lib/elixir/lib/task.ex#L279](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L279)
```elixir
@enforce_keys [:mfa, :owner, :pid, :ref]
@@ -303,7 +303,7 @@ defstruct @enforce_keys
}
```
**Source:** `lib/phoenix/router/route.ex:30-46`
**Source:** [lib/phoenix/router/route.ex#L30](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/router/route.ex#L30)
```elixir
defstruct [
@@ -389,14 +389,14 @@ end
## 5. Selective Imports in `__using__`
**Source:** `lib/phoenix/channel.ex:463-464`
**Source:** [lib/phoenix/channel.ex#L463](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/channel.ex#L463)
```elixir
import unquote(__MODULE__)
import Phoenix.Socket, only: [assign: 3, assign: 2]
```
**Source:** `lib/phoenix/router.ex:271-275`
**Source:** [lib/phoenix/router.ex#L271](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/router.ex#L271)
```elixir
import Phoenix.Router
@@ -471,7 +471,7 @@ end
## 6. Alias at Module Scope for Readability
**Source:** `lib/phoenix/router.ex:268`
**Source:** [lib/phoenix/router.ex#L268](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/router.ex#L268)
```elixir
alias Phoenix.Router.{Resource, Scope, Route, Helpers}
@@ -538,7 +538,7 @@ end
## 7. Boolean-Suffixed Fields in Structs
**Source:** `lib/phoenix/router/route.ex:43-44`
**Source:** [lib/phoenix/router/route.ex#L43](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/phoenix/router/route.ex#L43)
```elixir
:trailing_slash?,
@@ -589,3 +589,5 @@ defstruct [:user, :admin?, :count]
```
**Why:** The `?` suffix should only mark genuine booleans. Using it on non-boolean fields creates confusion about the field's type and breaks the convention's usefulness as a type signal.
<!-- PATTERN_COMPLETE -->
+20 -18
View File
@@ -6,7 +6,7 @@ Analysis of `lib/elixir/lib/supervisor.ex`, `lib/elixir/lib/dynamic_supervisor.e
## Pattern 1: Static vs Dynamic Supervision — Choose the Right Tool
**Source:** `lib/elixir/lib/supervisor.ex:1-20` vs `lib/elixir/lib/dynamic_supervisor.ex:1-20`
**Source:** [lib/elixir/lib/supervisor.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L1) vs [lib/elixir/lib/dynamic_supervisor.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/dynamic_supervisor.ex#L1)
**What it does:** Elixir provides two distinct supervisor types:
- `Supervisor` — for **static** children known at compile time, started in a defined order
@@ -110,7 +110,7 @@ end
## Pattern 2: PartitionSupervisor for Scalability
**Source:** `lib/elixir/lib/dynamic_supervisor.ex:60-95` and `lib/elixir/lib/task/supervisor.ex:35-65`
**Source:** [lib/elixir/lib/dynamic_supervisor.ex#L60](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/dynamic_supervisor.ex#L60) and [lib/elixir/lib/task/supervisor.ex#L35](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task/supervisor.ex#L35)
**What it does:** Both `DynamicSupervisor` and `Task.Supervisor` document the same scalability pattern: when a single supervisor becomes a bottleneck, wrap it in a `PartitionSupervisor` which starts N instances (one per core by default) and routes via a key.
@@ -206,7 +206,7 @@ end
## Pattern 3: Supervision Strategies — Choosing the Right Restart Behavior
**Source:** `lib/elixir/lib/supervisor.ex:315-345` (Strategies section)
**Source:** [lib/elixir/lib/supervisor.ex#L315](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L315) (Strategies section)
**What it does:** Three strategies model three dependency patterns:
- `:one_for_one` — independent children (crash of A doesn't affect B)
@@ -319,7 +319,7 @@ end
## Pattern 4: Restart Intensity (`max_restarts` / `max_seconds`)
**Source:** `lib/elixir/lib/supervisor.ex:309-313`, `lib/elixir/lib/dynamic_supervisor.ex:730-758` (implementation)
**Source:** [lib/elixir/lib/supervisor.ex#L309](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L309), [lib/elixir/lib/dynamic_supervisor.ex#L730](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/dynamic_supervisor.ex#L730) (implementation)
**What it does:** Supervisors track restart frequency. If a child exceeds `max_restarts` within `max_seconds`, the supervisor itself shuts down (escalating the failure to its parent). Defaults: 3 restarts in 5 seconds.
@@ -422,7 +422,7 @@ Supervisor.init(children,
## Pattern 5: Restart Values — `:permanent` vs `:transient` vs `:temporary`
**Source:** `lib/elixir/lib/supervisor.ex:130-152` (Restart values section)
**Source:** [lib/elixir/lib/supervisor.ex#L130](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L130) (Restart values section)
**What it does:** Three restart policies control what happens when a child terminates:
- `:permanent` — always restart (default for GenServer/Agent/Supervisor)
@@ -522,7 +522,7 @@ end
## Pattern 6: Automatic Shutdown for Pipeline Supervisors
**Source:** `lib/elixir/lib/supervisor.ex:349-375` (Automatic shutdown section)
**Source:** [lib/elixir/lib/supervisor.ex#L349](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L349) (Automatic shutdown section)
**What it does:** Supervisors support `:auto_shutdown` which terminates the supervisor when significant children exit. Options: `:any_significant` (first significant child exits → shutdown) or `:all_significant` (all significant children must exit → shutdown).
@@ -634,7 +634,7 @@ Supervisor.init(children, strategy: :one_for_one)
## Pattern 7: Task.async/await for Concurrent Value Computation
**Source:** `lib/elixir/lib/task.ex:1-20` and `lib/elixir/lib/task.ex:300-340`
**Source:** [lib/elixir/lib/task.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L1) and [lib/elixir/lib/task.ex#L300](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L300)
**What it does:** `Task.async` spawns a linked, monitored process and returns a `%Task{}` struct. `Task.await` blocks until the result arrives or times out. This is the canonical pattern for "compute a value concurrently."
@@ -718,7 +718,7 @@ end
## Pattern 8: Task.Supervisor.async_nolink for Fault-Tolerant Task Execution
**Source:** `lib/elixir/lib/task/supervisor.ex:240-320` (async_nolink docs with GenServer example)
**Source:** [lib/elixir/lib/task/supervisor.ex#L240](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task/supervisor.ex#L240) (async_nolink docs with GenServer example)
**What it does:** Unlike `Task.async`, `async_nolink` spawns a task that is NOT linked to the caller. The caller monitors it and handles success/failure via `handle_info`. This prevents a task crash from killing the caller.
@@ -844,7 +844,7 @@ end
## Pattern 9: Task Supervisor as DynamicSupervisor Specialization
**Source:** `lib/elixir/lib/task/supervisor.ex:151-165` (start_link implementation)
**Source:** [lib/elixir/lib/task/supervisor.ex#L151](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task/supervisor.ex#L151) (start_link implementation)
**What it does:** `Task.Supervisor` is implemented directly on top of `DynamicSupervisor`. It stores default restart/shutdown settings in the process dictionary and delegates `init` to `DynamicSupervisor.init`.
@@ -936,7 +936,7 @@ DynamicSupervisor.start_child(MyApp.WorkerSupervisor, {MyApp.Worker, args})
## Pattern 10: Registry for Dynamic Process Naming and PubSub
**Source:** `lib/elixir/lib/registry.ex:1-70` (module docs), `lib/elixir/lib/registry.ex:250-270` (whereis_name via callbacks)
**Source:** [lib/elixir/lib/registry.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/registry.ex#L1) (module docs), [lib/elixir/lib/registry.ex#L250](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/registry.ex#L250) (whereis_name via callbacks)
**What it does:** Registry provides two modes:
- `:unique` keys — each key maps to exactly one process (name registry, process lookup)
@@ -1053,7 +1053,7 @@ end
## Pattern 11: Shutdown Semantics — Graceful Termination
**Source:** `lib/elixir/lib/supervisor.ex:156-192` (Shutdown values section)
**Source:** [lib/elixir/lib/supervisor.ex#L156](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L156) (Shutdown values section)
**What it does:** Three shutdown modes:
- `:brutal_kill` — immediate `Process.exit(child, :kill)`, no cleanup
@@ -1155,7 +1155,7 @@ end
## Pattern 12: DynamicSupervisor Internal State — Struct with Restart Tracking
**Source:** `lib/elixir/lib/dynamic_supervisor.ex:165-178` (defstruct)
**Source:** [lib/elixir/lib/dynamic_supervisor.ex#L165](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/dynamic_supervisor.ex#L165) (defstruct)
**What it does:** The DynamicSupervisor uses a struct for its GenServer state with explicit fields: `children` (map of pid → child spec), `restarts` (list of timestamps for rate limiting), and configuration fields.
@@ -1255,7 +1255,7 @@ end
## Pattern 13: Restart Logic with Exponential Backoff via `:try_again`
**Source:** `lib/elixir/lib/dynamic_supervisor.ex:710-758` (restart_child and related functions)
**Source:** [lib/elixir/lib/dynamic_supervisor.ex#L710](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/dynamic_supervisor.ex#L710) (restart_child and related functions)
**What it does:** When a child fails to restart (start function returns error), DynamicSupervisor doesn't give up. It stores the child as `{:restarting, child}`, sends itself a `:"$gen_restart"` message, and retries later. This prevents the supervisor from blocking on a transiently failing child.
@@ -1382,7 +1382,7 @@ end
## Pattern 14: `$ancestors` and `$callers` — Process Lineage Tracking
**Source:** `lib/elixir/lib/task.ex:227-268` (Ancestor and Caller Tracking section)
**Source:** [lib/elixir/lib/task.ex#L227](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/task.ex#L227) (Ancestor and Caller Tracking section)
**What it does:** Elixir uses two process dictionary keys for lineage:
- `$ancestors` — the supervision hierarchy (who spawned/supervises this process)
@@ -1495,7 +1495,7 @@ end
## Pattern 15: GenServer.reply/2 for Deferred Responses
**Source:** `lib/elixir/lib/gen_server.ex:620-640` (callback docs), `lib/elixir/lib/gen_server.ex:1328-1346` (reply/2 function)
**Source:** [lib/elixir/lib/gen_server.ex#L620](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L620) (callback docs), [lib/elixir/lib/gen_server.ex#L1328](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L1328) (reply/2 function)
**What it does:** A `handle_call` can return `{:noreply, state}` without replying, then later call `GenServer.reply(from, response)` from any process. This decouples request receipt from response delivery.
@@ -1606,7 +1606,7 @@ end
## Pattern 16: Process.alias for Safe Request/Response
**Source:** `lib/elixir/lib/process.ex:32-95` (Aliases section)
**Source:** [lib/elixir/lib/process.ex#L32](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/process.ex#L32) (Aliases section)
**What it does:** Process aliases (Erlang/OTP 24+) provide a deactivatable reference for receiving replies. After sending a request with an alias as the reply address, you can deactivate the alias if you no longer want the response — any messages sent to a deactivated alias are silently dropped.
@@ -1714,7 +1714,7 @@ end
## Pattern 17: Registry Partitioning Strategies
**Source:** `lib/elixir/lib/registry.ex:310-350` (start_link partitioning docs)
**Source:** [lib/elixir/lib/registry.ex#L310](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/registry.ex#L310) (start_link partitioning docs)
**What it does:** Duplicate registries support two partitioning strategies:
- `{:duplicate, :pid}` (default) — groups entries by the registering process's PID. Good for few keys with many entries (e.g., one PubSub topic with many subscribers).
@@ -1812,7 +1812,7 @@ Registry.start_link(
## Pattern 18: `init/1` Return Values — The Full Spectrum
**Source:** `lib/elixir/lib/gen_server.ex:498-545` (init callback spec)
**Source:** [lib/elixir/lib/gen_server.ex#L498](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L498) (init callback spec)
**What it does:** `init/1` supports five return values:
- `{:ok, state}` — normal start
@@ -1910,3 +1910,5 @@ end
```
**Why:** `:ignore` means "this child intentionally should not run right now." `{:stop, reason}` means "this child tried to start and failed." Conflating the two hides real failures from your supervision tree.
<!-- PATTERN_COMPLETE -->
+22 -20
View File
@@ -6,7 +6,7 @@ Patterns extracted from the Elixir standard library source code — how the core
## 1. Module-Level Async Declaration
**Source:** `lib/elixir/test/elixir/gen_server_test.exs:9`, `lib/elixir/test/elixir/enum_test.exs:8`, nearly all test files
**Source:** [lib/elixir/test/elixir/gen_server_test.exs#L9](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/gen_server_test.exs#L9), [lib/elixir/test/elixir/enum_test.exs#L8](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/enum_test.exs#L8), nearly all test files
**What it does:** Every test module declares `async: true` or `async: false` at the module level, making concurrency intent explicit.
@@ -101,7 +101,7 @@ end
## 2. Parameterized Tests
**Source:** `lib/elixir/test/elixir/registry_test.exs:12-22`
**Source:** [lib/elixir/test/elixir/registry_test.exs#L12](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/registry_test.exs#L12)
**What it does:** Runs the same test suite against multiple configurations using the `:parameterize` option (since v1.18).
@@ -214,7 +214,7 @@ end
## 3. Setup with `start_supervised/2`
**Source:** `lib/ex_unit/lib/ex_unit/callbacks.ex:277-340`, `lib/elixir/test/elixir/registry_test.exs:31`
**Source:** [lib/ex_unit/lib/ex_unit/callbacks.ex#L277](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/callbacks.ex#L277), [lib/elixir/test/elixir/registry_test.exs#L31](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/registry_test.exs#L31)
**What it does:** Starts processes under a test supervisor that guarantees cleanup before the next test.
@@ -293,7 +293,7 @@ end
## 4. Named Setup Functions (Composable Pipelines)
**Source:** `lib/ex_unit/lib/ex_unit/callbacks.ex:100-120` (docs)
**Source:** [lib/ex_unit/lib/ex_unit/callbacks.ex#L100](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/callbacks.ex#L100) (docs)
**What it does:** Defines setup as a list of named functions rather than anonymous blocks.
@@ -364,7 +364,7 @@ end
## 5. `on_exit` for Reversing Global Side Effects
**Source:** `lib/elixir/test/elixir/task_test.exs:1128-1131`, `lib/logger/test/logger_test.exs:12-17`
**Source:** [lib/elixir/test/elixir/task_test.exs#L1128](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L1128), [lib/logger/test/logger_test.exs#L12](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/test/logger_test.exs#L12)
**What it does:** Registers cleanup callbacks that always run, even if the test fails.
@@ -437,7 +437,7 @@ end
## 6. Pattern Match Assertions
**Source:** `lib/ex_unit/lib/ex_unit/assertions.ex:145-175`
**Source:** [lib/ex_unit/lib/ex_unit/assertions.ex#L145](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/assertions.ex#L145)
**What it does:** Uses `assert` with `=` for structural pattern matching in assertions.
@@ -506,7 +506,7 @@ assert create_user("alice") == {:ok, %User{name: "alice", active: true}}
## 7. `assert_receive` / `refute_receive` for Process Communication
**Source:** `lib/ex_unit/lib/ex_unit/assertions.ex:466-526`, `lib/elixir/test/elixir/process_test.exs:90-100`
**Source:** [lib/ex_unit/lib/ex_unit/assertions.ex#L466](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/assertions.ex#L466), [lib/elixir/test/elixir/process_test.exs#L90](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/process_test.exs#L90)
**What it does:** Waits for messages matching a pattern within a timeout (default 100ms).
@@ -590,7 +590,7 @@ end
## 8. Testing GenServers via Public API (No Internal State Inspection)
**Source:** `lib/elixir/test/elixir/gen_server_test.exs:87-106`
**Source:** [lib/elixir/test/elixir/gen_server_test.exs#L87](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/gen_server_test.exs#L87)
**What it does:** Tests GenServer behavior exclusively through `GenServer.call/cast/stop` — never peeks at internal state.
@@ -670,7 +670,7 @@ end
## 9. `catch_exit` for Testing Process Failures
**Source:** `lib/ex_unit/lib/ex_unit/assertions.ex:950-960`, `lib/elixir/test/elixir/gen_server_test.exs:118-137`
**Source:** [lib/ex_unit/lib/ex_unit/assertions.ex#L950](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/assertions.ex#L950), [lib/elixir/test/elixir/gen_server_test.exs#L118](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/gen_server_test.exs#L118)
**What it does:** Catches exit signals from linked processes for assertion, or uses `Process.flag(:trap_exit, true)` + `assert_receive {:EXIT, ...}`.
@@ -747,7 +747,7 @@ end
## 10. `@tag capture_log: true` for Suppressing Expected Log Output
**Source:** `lib/elixir/test/elixir/gen_server_test.exs:114`, `lib/elixir/test/elixir/task_test.exs:10`
**Source:** [lib/elixir/test/elixir/gen_server_test.exs#L114](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/gen_server_test.exs#L114), [lib/elixir/test/elixir/task_test.exs#L10](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L10)
**What it does:** Captures log output during the test, only printing it if the test fails.
@@ -833,7 +833,7 @@ end
## 11. `capture_log` / `capture_io` for Content Assertions
**Source:** `lib/ex_unit/lib/ex_unit/capture_log.ex:1-50`, `lib/elixir/test/elixir/task_test.exs:1138-1150`
**Source:** [lib/ex_unit/lib/ex_unit/capture_log.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/capture_log.ex#L1), [lib/elixir/test/elixir/task_test.exs#L1138](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L1138)
**What it does:** Captures log/IO output and returns it as a string for assertion.
@@ -918,7 +918,7 @@ end
## 12. `describe` Blocks for Logical Grouping
**Source:** `lib/elixir/test/elixir/task_test.exs:218,272,365`, `lib/elixir/test/elixir/process_test.exs:146`
**Source:** `lib/elixir/test/elixir/task_test.exs:218,272,365`, [lib/elixir/test/elixir/process_test.exs#L146](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/process_test.exs#L146)
**What it does:** Groups related tests under a named describe block. Setup inside describe only applies to that group.
@@ -1000,7 +1000,7 @@ end
## 13. `ExUnit.CaseTemplate` for Shared Test Infrastructure
**Source:** `lib/mix/test/test_helper.exs:79-140`, `lib/logger/test/test_helper.exs:24-65`
**Source:** [lib/mix/test/test_helper.exs#L79](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/mix/test/test_helper.exs#L79), [lib/logger/test/test_helper.exs#L24](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/test/test_helper.exs#L24)
**What it does:** Defines reusable test case templates with shared setup, helpers, and imports.
@@ -1124,7 +1124,7 @@ defmodule MyApp.ChannelCase do ... end # WebSocket tests
## 14. `doctest` Integration
**Source:** `lib/ex_unit/lib/ex_unit/doc_test.ex:1-80`, `lib/elixir/test/elixir/agent_test.exs:9`
**Source:** [lib/ex_unit/lib/ex_unit/doc_test.ex#L1](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/doc_test.ex#L1), [lib/elixir/test/elixir/agent_test.exs#L9](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/agent_test.exs#L9)
**What it does:** Generates tests from `@doc` and `@moduledoc` code examples.
@@ -1209,7 +1209,7 @@ Creates a user.
## 15. `Process.sleep(:infinity)` as a Process Parking Pattern
**Source:** `lib/elixir/test/elixir/task_test.exs:417`, `lib/elixir/test/elixir/registry_test.exs:71`
**Source:** [lib/elixir/test/elixir/task_test.exs#L417](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L417), [lib/elixir/test/elixir/registry_test.exs#L71](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/registry_test.exs#L71)
**What it does:** Spawns processes that block forever, used as test subjects that need to exist until explicitly killed.
@@ -1297,7 +1297,7 @@ end
## 16. Helper Functions for Test-Specific Behavior
**Source:** `lib/elixir/test/elixir/task_test.exs:12-36`, `lib/elixir/test/elixir/supervisor_test.exs:278-285`
**Source:** [lib/elixir/test/elixir/task_test.exs#L12](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L12), [lib/elixir/test/elixir/supervisor_test.exs#L278](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/supervisor_test.exs#L278)
**What it does:** Defines private helper functions within test modules for common test operations.
@@ -1409,7 +1409,7 @@ end
## 17. `@tag :tmp_dir` for Filesystem Tests
**Source:** `lib/ex_unit/lib/ex_unit/case.ex:281-304`, `lib/elixir/test/elixir/path_test.exs:12`
**Source:** [lib/ex_unit/lib/ex_unit/case.ex#L281](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/case.ex#L281), [lib/elixir/test/elixir/path_test.exs#L12](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/path_test.exs#L12)
**What it does:** ExUnit automatically creates a unique temporary directory and passes its path via the test context.
@@ -1482,7 +1482,7 @@ end
## 18. `assert_raise` with Message Matching
**Source:** `lib/ex_unit/lib/ex_unit/assertions.ex:815-885`
**Source:** [lib/ex_unit/lib/ex_unit/assertions.ex#L815](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/assertions.ex#L815)
**What it does:** Asserts both the exception type AND the message content (string or regex).
@@ -1564,7 +1564,7 @@ end
## 19. `@moduletag` / `@describetag` for Cross-Cutting Configuration
**Source:** `lib/elixir/test/elixir/system_test.exs:104,163`, `lib/elixir/test/elixir/task_test.exs:10`
**Source:** `lib/elixir/test/elixir/system_test.exs:104,163`, [lib/elixir/test/elixir/task_test.exs#L10](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/task_test.exs#L10)
**What it does:** Sets tags that apply to all tests in a module or describe block, used for filtering and configuration.
@@ -1651,7 +1651,7 @@ ExUnit.configure(exclude: [:unix], include: [])
## 20. Context Pattern Matching in Test Signatures
**Source:** `lib/ex_unit/lib/ex_unit/case.ex:57-80`, `lib/elixir/test/elixir/gen_server_test.exs:166`
**Source:** [lib/ex_unit/lib/ex_unit/case.ex#L57](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/ex_unit/lib/ex_unit/case.ex#L57), [lib/elixir/test/elixir/gen_server_test.exs#L166](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/test/elixir/gen_server_test.exs#L166)
**What it does:** Destructures the test context directly in the test function signature.
@@ -1724,3 +1724,5 @@ end
```
**Why:** Context destructuring signals "this test depends on external setup." If the test is self-contained, the pattern match is misleading — readers will look for setup that doesn't exist or isn't needed.
<!-- PATTERN_COMPLETE -->
+12 -10
View File
@@ -6,7 +6,7 @@ Patterns extracted from the Elixir standard library source code.
## 1. Public Type with @typedoc
**Source:** `lib/elixir/lib/gen_server.ex` lines 862896
**Source:** [lib/elixir/lib/gen_server.ex#L862](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L862)
**What it does:** Every public `@type` is preceded by a `@typedoc` that explains what the type represents, often referencing which functions use it.
@@ -91,7 +91,7 @@ the current state of a user account.
## 2. Private Types with @typep
**Source:** `lib/elixir/lib/macro.ex` lines 84, 97
**Source:** [lib/elixir/lib/macro.ex#L84](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/macro.ex#L84), 97
**What it does:** Uses `@typep` for internal recursive type definitions that are implementation details not meant for external consumers.
@@ -190,7 +190,7 @@ end
## 3. @opaque Types (Protocol t())
**Source:** `lib/elixir/lib/protocol.ex` lines 150168 (documentation), runtime generation
**Source:** [lib/elixir/lib/protocol.ex#L150](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/protocol.ex#L150) (documentation), runtime generation
**What it does:** Protocols auto-generate an opaque `@type t :: term()` type that represents "any value implementing this protocol."
@@ -269,7 +269,7 @@ end
## 4. Union Types in @spec Return Values
**Source:** `lib/elixir/lib/gen_server.ex` lines 577583, 647658
**Source:** [lib/elixir/lib/gen_server.ex#L577](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L577), 647658
**What it does:** Uses union types with descriptive tagged tuples in callback specs, making all possible return shapes explicit.
@@ -355,7 +355,7 @@ end
## 5. `when` Constraints in Specs
**Source:** `lib/elixir/lib/kernel.ex` lines 635, 1072, 1455
**Source:** [lib/elixir/lib/kernel.ex#L635](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/kernel.ex#L635), 1072, 1455
**What it does:** Uses the `when` clause in `@spec` to bind type variables, expressing relationships between parameters and return values.
@@ -414,7 +414,7 @@ end
## 6. Map Types with required/optional Keys
**Source:** `lib/elixir/lib/supervisor.ex` lines 602607, 644652
**Source:** [lib/elixir/lib/supervisor.ex#L602](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L602), 644652
**What it does:** Uses map type syntax with `required()` and `optional()` keys to define struct-like specs where some fields have defaults.
@@ -499,7 +499,7 @@ end
## 7. Keyword List Types for Options
**Source:** `lib/logger/lib/logger.ex` lines 509531
**Source:** [lib/logger/lib/logger.ex#L509](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/logger/lib/logger.ex#L509)
**What it does:** Defines option types as keyword lists with specific key-value constraints, sometimes nested.
@@ -588,7 +588,7 @@ end
## 8. Parameterized Types (t/1)
**Source:** `lib/elixir/lib/enum.ex` lines 5873 (Enumerable protocol)
**Source:** [lib/elixir/lib/enum.ex#L58](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L58) (Enumerable protocol)
**What it does:** Defines a parameterized type `t(_element)` that allows expressing the element type of an enumerable in function specs.
@@ -670,7 +670,7 @@ integers and returns an enumerable of strings:
## 9. Named Parameters in Specs (:: annotation)
**Source:** `lib/elixir/lib/gen_server.ex` line 577, `lib/elixir/lib/supervisor.ex` line 562
**Source:** [lib/elixir/lib/gen_server.ex#L577](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/gen_server.ex#L577), [lib/elixir/lib/supervisor.ex#L562](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L562)
**What it does:** Uses `name :: type` syntax in callback/spec parameter positions to give meaningful names to parameters.
@@ -735,7 +735,7 @@ integers and returns an enumerable of strings:
## 10. @typedoc since: Annotation
**Source:** `lib/elixir/lib/supervisor.ex` lines 669670, `lib/elixir/lib/enum.ex` line 72
**Source:** [lib/elixir/lib/supervisor.ex#L669](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/supervisor.ex#L669), [lib/elixir/lib/enum.ex#L72](https://github.com/elixir-lang/elixir/blob/f4e1b34617ef92052b65781f18eae5b88a490098/lib/elixir/lib/enum.ex#L72)
**What it does:** Attaches a `since:` metadata annotation to `@typedoc` indicating when a type was introduced.
@@ -797,3 +797,5 @@ end
```
**Why:** `since:` annotations are for library consumers checking compatibility across versions. Application code doesn't have "consumers" checking which version introduced a type — it's all deployed together.
<!-- PATTERN_COMPLETE -->