Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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 on [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
65 changes: 65 additions & 0 deletions lib/elixir/pages/getting-started/compilation-and-scripting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<!--
SPDX-License-Identifier: Apache-2.0
SPDX-FileCopyrightText: 2021 The Elixir Team
-->

# Compilation and Scripting

As our examples get longer in size, it can be tricky to type them all in the shell. It's about time for us to learn how to compile Elixir code and also how to run Elixir scripts.

## Compilation

Most of the time it is convenient to write [modules](modules-and-functions.md) (more closely looked at in the next chapter) into files so they can be compiled and reused. Let's assume we have a file named `math.ex` with the following contents:

```elixir
defmodule Math do
def sum(a, b) do
a + b
end
end
```

This file can be compiled using `elixirc`:

```console
$ elixirc math.ex
```

This will generate a file named `Elixir.Math.beam` containing the bytecode for the defined module. If we start `iex` again, our module definition will be available (provided that `iex` is started in the same directory the bytecode file is in):

```elixir
iex> Math.sum(1, 2)
3
```

## Scripting mode

In addition to the Elixir file extension `.ex`, Elixir also supports `.exs` files for scripting. Elixir treats both files exactly the same way, the only difference is in intention. `.ex` files are meant to be compiled while `.exs` files are used for scripting. This convention is followed by projects like `mix`.

For instance, we can create a file called `math.exs`:

```elixir
defmodule Math do
def sum(a, b) do
a + b
end
end

IO.puts Math.sum(1, 2)
```

And execute it as:

```console
$ elixir math.exs
```

Because we used `elixir` instead of `elixirc`, the module was compiled and loaded into memory, but no `.beam` file was written to disk.

Elixir projects are usually organized into three directories:

- `_build` - contains compilation artifacts
- `lib` - contains Elixir code (usually `.ex` files)
- `test` - contains tests (usually `.exs` files)

When working on actual projects, the build tool called `mix` will be responsible for compiling and setting up the proper paths for you. For learning and convenience purposes, we recommend you to write the following code into script files and execute them as shown above.
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) in elixir, 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
139 changes: 78 additions & 61 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 All @@ -25,64 +25,7 @@ iex> Math.sum(1, 2)
3
```

In this chapter we will define our own modules, with different levels of complexity. As our examples get longer in size, it can be tricky to type them all in the shell. It's about time for us to learn how to compile Elixir code and also how to run Elixir scripts.

## Compilation

Most of the time it is convenient to write modules into files so they can be compiled and reused. Let's assume we have a file named `math.ex` with the following contents:

```elixir
defmodule Math do
def sum(a, b) do
a + b
end
end
```

This file can be compiled using `elixirc`:

```console
$ elixirc math.ex
```

This will generate a file named `Elixir.Math.beam` containing the bytecode for the defined module. If we start `iex` again, our module definition will be available (provided that `iex` is started in the same directory the bytecode file is in):

```elixir
iex> Math.sum(1, 2)
3
```

## Scripting mode

In addition to the Elixir file extension `.ex`, Elixir also supports `.exs` files for scripting. Elixir treats both files exactly the same way, the only difference is in intention. `.ex` files are meant to be compiled while `.exs` files are used for scripting. This convention is followed by projects like `mix`.

For instance, we can create a file called `math.exs`:

```elixir
defmodule Math do
def sum(a, b) do
a + b
end
end

IO.puts Math.sum(1, 2)
```

And execute it as:

```console
$ elixir math.exs
```

Because we used `elixir` instead of `elixirc`, the module was compiled and loaded into memory, but no `.beam` file was written to disk.

Elixir projects are usually organized into three directories:

* `_build` - contains compilation artifacts
* `lib` - contains Elixir code (usually `.ex` files)
* `test` - contains tests (usually `.exs` files)

When working on actual projects, the build tool called `mix` will be responsible for compiling and setting up the proper paths for you. For learning and convenience purposes, we recommend you to write the following code into script files and execute them as shown above.
In this chapter we will define our own modules, with different levels of complexity.

## Function definition

Expand Down Expand Up @@ -126,7 +69,7 @@ The trailing question mark in `zero?` means that this function returns a boolean

Giving an argument that does not match any of the clauses raises an error.

Similar to constructs like `if`, function definitions support both `do:` and `do`-block syntax, as [we learned in the previous chapter](keywords-and-maps.md#do-blocks-and-keywords). For example, we can edit `math.exs` to look like this:
Similar to constructs like `if`, function definitions support both `do:` and `do`-block syntax, as [we learned in an earlier chapter](keywords-and-maps.md#do-blocks-and-keywords). For example, we can edit `math.exs` to look like this:

```elixir
defmodule Math do
Expand Down Expand Up @@ -194,4 +137,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.
Loading