diff --git a/lib/elixir/pages/getting-started/alias-require-and-import.md b/lib/elixir/pages/getting-started/alias-require-and-import.md index f77b78efae7..31d1564960e 100644 --- a/lib/elixir/pages/getting-started/alias-require-and-import.md +++ b/lib/elixir/pages/getting-started/alias-require-and-import.md @@ -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: @@ -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. diff --git a/lib/elixir/pages/getting-started/anonymous-functions.md b/lib/elixir/pages/getting-started/anonymous-functions.md index bec912d000e..253ba3e650e 100644 --- a/lib/elixir/pages/getting-started/anonymous-functions.md +++ b/lib/elixir/pages/getting-started/anonymous-functions.md @@ -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`. diff --git a/lib/elixir/pages/getting-started/case-cond-and-if.md b/lib/elixir/pages/getting-started/case-cond-and-if.md index 2da7c568afd..9c354105e5c 100644 --- a/lib/elixir/pages/getting-started/case-cond-and-if.md +++ b/lib/elixir/pages/getting-started/case-cond-and-if.md @@ -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 @@ -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. diff --git a/lib/elixir/pages/getting-started/keywords-and-maps.md b/lib/elixir/pages/getting-started/keywords-and-maps.md index 040a47bedc8..656e84c96ca 100644 --- a/lib/elixir/pages/getting-started/keywords-and-maps.md +++ b/lib/elixir/pages/getting-started/keywords-and-maps.md @@ -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" ``` @@ -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. diff --git a/lib/elixir/pages/getting-started/modules-and-functions.md b/lib/elixir/pages/getting-started/modules-and-functions.md index 25263746508..7f8bd85adf0 100644 --- a/lib/elixir/pages/getting-started/modules-and-functions.md +++ b/lib/elixir/pages/getting-started/modules-and-functions.md @@ -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 @@ -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. diff --git a/lib/elixir/pages/getting-started/pattern-matching.md b/lib/elixir/pages/getting-started/pattern-matching.md index 8b114791d53..78dde7c0ef2 100644 --- a/lib/elixir/pages/getting-started/pattern-matching.md +++ b/lib/elixir/pages/getting-started/pattern-matching.md @@ -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 @@ -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. diff --git a/lib/elixir/pages/references/patterns-and-guards.md b/lib/elixir/pages/references/patterns-and-guards.md index 551fe29b6b1..2029a495ca4 100644 --- a/lib/elixir/pages/references/patterns-and-guards.md +++ b/lib/elixir/pages/references/patterns-and-guards.md @@ -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: diff --git a/lib/elixir/scripts/elixir_docs.exs b/lib/elixir/scripts/elixir_docs.exs index 74571ac0f8d..36478cc00a6 100644 --- a/lib/elixir/scripts/elixir_docs.exs +++ b/lib/elixir/scripts/elixir_docs.exs @@ -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",