Skip to content

Docs updates und restructuring #14636

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jul 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 1 addition & 78 deletions lib/elixir/pages/getting-started/alias-require-and-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,83 +156,6 @@ end

Since `use` allows any code to run, we can't really know the side-effects of using a module without reading its documentation. Therefore use this function with care and only if strictly required. Don't use `use` where an `import` or `alias` would do.

## Understanding Aliases

At this point, you may be wondering: what exactly is an Elixir alias and how is it represented?

An alias in Elixir is a capitalized identifier (like `String`, `Keyword`, etc) which is converted to an atom during compilation. For instance, the `String` alias translates by default to the atom `:"Elixir.String"`:

```elixir
iex> is_atom(String)
true
iex> to_string(String)
"Elixir.String"
iex> :"Elixir.String" == String
true
```

By using the `alias/2` directive, we are changing the atom the alias expands to.

Aliases expand to atoms because in the Erlang Virtual Machine (and consequently Elixir) modules are always represented by atoms:

```elixir
iex> List.flatten([1, [2], 3])
[1, 2, 3]
iex> :"Elixir.List".flatten([1, [2], 3])
[1, 2, 3]
```

That's the mechanism we use to call Erlang modules:

```elixir
iex> :lists.flatten([1, [2], 3])
[1, 2, 3]
```

## Module nesting

Now that we have talked about aliases, we can talk about nesting and how it works in Elixir. Consider the following example:

```elixir
defmodule Foo do
defmodule Bar do
end
end
```

The example above will define two modules: `Foo` and `Foo.Bar`. The second can be accessed as `Bar` inside `Foo` as long as they are in the same lexical scope.

If, later, the `Bar` module is moved outside the `Foo` module definition, it must be referenced by its full name (`Foo.Bar`) or an alias must be set using the `alias` directive discussed above.

**Note**: in Elixir, you don't have to define the `Foo` module before being able to define the `Foo.Bar` module, as they are effectively independent. The above could also be written as:

```elixir
defmodule Foo.Bar do
end

defmodule Foo do
alias Foo.Bar
# Can still access it as `Bar`
end
```

Aliasing a nested module does not bring parent modules into scope. Consider the following example:

```elixir
defmodule Foo do
defmodule Bar do
defmodule Baz do
end
end
end

alias Foo.Bar.Baz
# The module `Foo.Bar.Baz` is now available as `Baz`
# However, the module `Foo.Bar` is *not* available as `Bar`
```

As we will see in later chapters, aliases also play a crucial role in macros, to guarantee they are hygienic.

## Multi alias/import/require/use

It is possible to `alias`, `import`, `require`, or `use` multiple modules at once. This is particularly useful once we start nesting modules, which is very common when building Elixir applications. For example, imagine you have an application where all modules are nested under `MyApp`, you can alias the modules `MyApp.Foo`, `MyApp.Bar` and `MyApp.Baz` at once as follows:
Expand All @@ -241,4 +164,4 @@ It is possible to `alias`, `import`, `require`, or `use` multiple modules at onc
alias MyApp.{Foo, Bar, Baz}
```

With this, we have finished our tour of Elixir modules. The next topic to cover is module attributes.
With this, we have finished our tour of Elixir modules.
2 changes: 1 addition & 1 deletion lib/elixir/pages/getting-started/anonymous-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Anonymous functions allow us to store and pass executable code around as if it w

## Identifying functions and documentation

Before we move on to discuss anonymous functions, let's talk about how Elixir identifies named functions.
Before we move on to discuss anonymous functions, let's talk about how Elixir identifies named functions – the functions defined in [modules](modules-and-functions.md).

Functions in Elixir are identified by both their name and their arity. The arity of a function describes the number of arguments that the function takes. From this point on we will use both the function name and its arity to describe functions throughout the documentation. `trunc/1` identifies the function which is named `trunc` and takes `1` argument, whereas `trunc/2` identifies a different (nonexistent) function with the same name but with an arity of `2`.

Expand Down
21 changes: 14 additions & 7 deletions lib/elixir/pages/getting-started/case-cond-and-if.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ iex> if nil do
"This will"
```

This is also a good opportunity to talk about variable scoping in Elixir. If any variable is declared or changed inside [`if`](`if/2`), [`case`](`case/2`), and similar constructs, the declaration and change will only be visible inside the construct. For example:
### Expressions

Some programming languages make a distinction about expressions (code that returns a value) and statements (code that returns no value). In Elixir, there are only expressions, no statements. Everything you write in Elixir language returns some value.

This property allows variables to be scoped to individual blocks of code such as [`if`](`if/2`), [`case`](`case/2`), where declarations or changes are only visible inside the block. A change can't leak to outer blocks, which makes code easier to follow and understand. For example:

```elixir
iex> x = 1
Expand All @@ -113,19 +117,22 @@ iex> x
1
```

In said cases, if you want to change a value, you must return the value from the [`if`](`if/2`):
You see the return value of the [`if`](`if/2`) expression as the resulting `2` here. To retain changes made within the [`if`](`if/2`) expression on the outer block you need to assign the returned value to a variable in the outer block.

```elixir
iex> x = 1
1
iex> x = if true do
...> x + 1
...> else
...> x
...> end
iex> x =
...> if true do
...> x + 1
...> else
...> x
...> end
2
```

With all expressions returning a value there's also no need for alternative constructs, such as ternary operators posing as an alternative to [`if`](`if/2`). Elixir does include an inline notation for [`if`](`if/2`) and, as we will [learn later](keywords-and-maps.md#do-blocks-and-keywords), it is a syntactic variation on `if`'s arguments.

> #### `if` is a macro {: .info}
>
> An interesting note regarding [`if`](`if/2`) is that it is implemented as a macro in the language: it isn't a special language construct as it would be in many languages. You can check the documentation and its source for more information.
Expand Down
4 changes: 3 additions & 1 deletion lib/elixir/pages/getting-started/keywords-and-maps.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ iex> if true do
In the example above, the `do` and `else` blocks make up a keyword list. They are nothing more than a syntax convenience on top of keyword lists. We can rewrite the above to:

```elixir
iex> if true, do: "This will be seen", else: "This won't"
iex> if(true, do: "This will be seen", else: "This won't")
"This will be seen"
```

Expand Down Expand Up @@ -225,6 +225,8 @@ These operations have one large benefit in that they raise if the key does not e

Elixir developers typically prefer to use the `map.key` syntax and pattern matching instead of the functions in the `Map` module when working with maps because they lead to an assertive style of programming. [This blog post by José Valim](https://dashbit.co/blog/writing-assertive-code-with-elixir) provides insight and examples on how you get more concise and faster software by writing assertive code in Elixir.

In a further chapter you'll learn about ["Structs"](structs.md), which further enforce the idea of a map with predefined keys.

## Nested data structures

Often we will have maps inside maps, or even keywords lists inside maps, and so forth. Elixir provides conveniences for manipulating nested data structures via the `get_in/1`, `put_in/2`, `update_in/2`, and other macros giving the same conveniences you would find in imperative languages while keeping the immutable properties of the language.
Expand Down
78 changes: 76 additions & 2 deletions lib/elixir/pages/getting-started/modules-and-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ iex> String.length("hello")
5
```

In order to create our own modules in Elixir, we use the [`defmodule`](`defmodule/2`) macro. The first letter of the module must be in uppercase. We use the [`def`](`def/2`) macro to define functions in that module. The first letter of every function must be in lowercase (or underscore):
In order to create our own modules in Elixir, we use the [`defmodule`](`defmodule/2`) macro. The first letter of an module name (an alias, as described further down) must be in uppercase. We use the [`def`](`def/2`) macro to define functions in that module. The first letter of every function must be in lowercase (or underscore):

```elixir
iex> defmodule Math do
Expand Down Expand Up @@ -194,4 +194,78 @@ IO.puts(Concat.join("Hello", "world", "_")) #=> Hello_world

When a variable is not used by a function or a clause, we add a leading underscore (`_`) to its name to signal this intent. This rule is also covered in our [Naming Conventions](../references/naming-conventions.md#underscore-_foo) document.

This finishes our short introduction to modules. In the next chapters, we will learn how to use function definitions for recursion and later on explore more functionality related to modules.
## Understanding Aliases

An alias in Elixir is a capitalized identifier (like `String`, `Keyword`, etc) which is converted to an atom during compilation. For instance, the `String` alias translates by default to the atom `:"Elixir.String"`:

```elixir
iex> is_atom(String)
true
iex> to_string(String)
"Elixir.String"
iex> :"Elixir.String" == String
true
```

By using the `alias/2` directive, we are changing the atom the alias expands to.

Aliases expand to atoms because in the Erlang Virtual Machine (and consequently Elixir) modules are always represented by atoms. By namespacing
those atoms elixir modules avoid conflicting with existing erlang modules.

```elixir
iex> List.flatten([1, [2], 3])
[1, 2, 3]
iex> :"Elixir.List".flatten([1, [2], 3])
[1, 2, 3]
```

That's the mechanism we use to call Erlang modules:

```elixir
iex> :lists.flatten([1, [2], 3])
[1, 2, 3]
```

## Module nesting

Now that we have talked about aliases, we can talk about nesting and how it works in Elixir. Consider the following example:

```elixir
defmodule Foo do
defmodule Bar do
end
end
```

The example above will define two modules: `Foo` and `Foo.Bar`. The second can be accessed as `Bar` inside `Foo` as long as they are in the same lexical scope.

If, later, the `Bar` module is moved outside the `Foo` module definition, it must be referenced by its full name (`Foo.Bar`) or an alias must be set using the `alias` directive discussed above.

**Note**: in Elixir, you don't have to define the `Foo` module before being able to define the `Foo.Bar` module, as they are effectively independent. The above could also be written as:

```elixir
defmodule Foo.Bar do
end

defmodule Foo do
alias Foo.Bar
# Can still access it as `Bar`
end
```

Aliasing a nested module does not bring parent modules into scope. Consider the following example:

```elixir
defmodule Foo do
defmodule Bar do
defmodule Baz do
end
end
end

alias Foo.Bar.Baz
# The module `Foo.Bar.Baz` is now available as `Baz`
# However, the module `Foo.Bar` is *not* available as `Bar`
```

As we will see in later chapters, aliases also play a crucial role in macros, to guarantee they are hygienic.
64 changes: 32 additions & 32 deletions lib/elixir/pages/getting-started/pattern-matching.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,38 @@ iex> [0 | list]
[0, 1, 2, 3]
```

In some cases, you don't care about a particular value in a pattern. It is a common practice to bind those values to the underscore, `_`. For example, if only the head of the list matters to us, we can assign the tail to underscore:

```elixir
iex> [head | _] = [1, 2, 3]
[1, 2, 3]
iex> head
1
```

The variable `_` is special in that it can never be read from. Trying to read from it gives a compile error:

```elixir
iex> _
** (CompileError) iex:1: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
```

If a variable is mentioned more than once in a pattern, all references must bind to the same value:

```elixir
iex> {x, x} = {1, 1}
{1, 1}
iex> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}
```

Although pattern matching allows us to build powerful constructs, its usage is limited. For instance, you cannot make function calls on the left side of a match. The following example is invalid:

```elixir
iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match
```

Pattern matching allows developers to easily destructure data types such as tuples and lists. As we will see in the following chapters, it is one of the foundations of recursion in Elixir and applies to other types as well, like maps and binaries.

## The pin operator
Expand Down Expand Up @@ -168,36 +200,4 @@ iex> {y, 1} = {2, 2}
** (MatchError) no match of right hand side value: {2, 2}
```

If a variable is mentioned more than once in a pattern, all references must bind to the same value:

```elixir
iex> {x, x} = {1, 1}
{1, 1}
iex> {x, x} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}
```

In some cases, you don't care about a particular value in a pattern. It is a common practice to bind those values to the underscore, `_`. For example, if only the head of the list matters to us, we can assign the tail to underscore:

```elixir
iex> [head | _] = [1, 2, 3]
[1, 2, 3]
iex> head
1
```

The variable `_` is special in that it can never be read from. Trying to read from it gives a compile error:

```elixir
iex> _
** (CompileError) iex:1: invalid use of _. "_" represents a value to be ignored in a pattern and cannot be used in expressions
```

Although pattern matching allows us to build powerful constructs, its usage is limited. For instance, you cannot make function calls on the left side of a match. The following example is invalid:

```elixir
iex> length([1, [2], 3]) = 3
** (CompileError) iex:1: cannot invoke remote function :erlang.length/1 inside match
```

This finishes our introduction to pattern matching. As we will see in the next chapter, pattern matching is very common in many language constructs and they can be further augmented with guards.
13 changes: 13 additions & 0 deletions lib/elixir/pages/references/patterns-and-guards.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ iex> _
** (CompileError) iex:3: invalid use of _
```

A pinned value represents the value itself and not its – even if syntatically equal – pattern. The right hand side is compared to be equal to the pinned value:

```iex
iex> x = %{}
%{}
iex> {:ok, %{}} = {:ok, %{a: 13}}
{:ok, %{a: 13}}
iex> {:ok, ^x} = {:ok, %{a: 13}}
** (MatchError) no match of right hand side value: {:ok, %{a: 13}}
(stdlib 6.2) erl_eval.erl:667: :erl_eval.expr/6
iex:2: (file)
```

### Literals (numbers and atoms)

Atoms and numbers (integers and floats) can appear in patterns and they are always represented as is. For example, an atom will only match an atom if they are the same atom:
Expand Down
10 changes: 5 additions & 5 deletions lib/elixir/scripts/elixir_docs.exs
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ canonical = System.fetch_env!("CANONICAL")
"lib/elixir/pages/getting-started/binaries-strings-and-charlists.md",
"lib/elixir/pages/getting-started/keywords-and-maps.md",
"lib/elixir/pages/getting-started/modules-and-functions.md",
"lib/elixir/pages/getting-started/recursion.md",
"lib/elixir/pages/getting-started/enumerable-and-streams.md",
"lib/elixir/pages/getting-started/processes.md",
"lib/elixir/pages/getting-started/io-and-the-file-system.md",
"lib/elixir/pages/getting-started/alias-require-and-import.md",
"lib/elixir/pages/getting-started/module-attributes.md",
"lib/elixir/pages/getting-started/structs.md",
"lib/elixir/pages/getting-started/protocols.md",
"lib/elixir/pages/getting-started/recursion.md",
"lib/elixir/pages/getting-started/enumerable-and-streams.md",
"lib/elixir/pages/getting-started/comprehensions.md",
"lib/elixir/pages/getting-started/protocols.md",
"lib/elixir/pages/getting-started/sigils.md",
"lib/elixir/pages/getting-started/try-catch-and-rescue.md",
"lib/elixir/pages/getting-started/processes.md",
"lib/elixir/pages/getting-started/io-and-the-file-system.md",
"lib/elixir/pages/getting-started/writing-documentation.md",
"lib/elixir/pages/getting-started/optional-syntax.md",
"lib/elixir/pages/getting-started/erlang-libraries.md",
Expand Down