Skip to content

Commit 7b19eac

Browse files
author
José Valim
committed
Merge default arguments into the module chapter
1 parent 0c83aea commit 7b19eac

File tree

14 files changed

+852
-850
lines changed

14 files changed

+852
-850
lines changed

‎_layouts/getting_started.html‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,18 @@ <h3 class="widget-title">Index</h3>
1919
<li><aclass="spec" href="/getting_started/6.html">Binaries, strings and char lists</a></li>
2020
<li><aclass="spec" href="/getting_started/7.html">Keywords, maps and dicts</a></li>
2121
<li><aclass="spec" href="/getting_started/8.html">Modules</a></li>
22-
<li><aclass="spec" href="/getting_started/9.html">Default arguments</a></li>
23-
<li><aclass="spec" href="/getting_started/10.html">Recursion</a></li>
24-
<li><aclass="spec" href="/getting_started/11.html">alias, require and import</a></li>
25-
<li><aclass="spec" href="/getting_started/12.html">Module attributes</a></li>
26-
<li><aclass="spec" href="/getting_started/13.html">Enumerables and streams</a></li>
27-
<li><aclass="spec" href="/getting_started/14.html">Processes</a></li>
28-
<li><aclass="spec" href="/getting_started/15.html">IO</a></li>
29-
<li><aclass="spec" href="/getting_started/16.html">Structs</a></li>
30-
<li><aclass="spec" href="/getting_started/17.html">Protocols</a></li>
31-
<li><aclass="spec" href="/getting_started/18.html">try, catch and rescue</a></li>
32-
<li><aclass="spec" href="/getting_started/19.html">Comprehensions</a></li>
33-
<li><aclass="spec" href="/getting_started/20.html">Sigils</a></li>
22+
<li><aclass="spec" href="/getting_started/9.html">Recursion</a></li>
23+
<li><aclass="spec" href="/getting_started/10.html">alias, require and import</a></li>
24+
<li><aclass="spec" href="/getting_started/11.html">Module attributes</a></li>
25+
<li><aclass="spec" href="/getting_started/12.html">Enumerables and streams</a></li>
26+
<li><aclass="spec" href="/getting_started/13.html">Processes</a></li>
27+
<li><aclass="spec" href="/getting_started/14.html">IO</a></li>
28+
<li><aclass="spec" href="/getting_started/15.html">Structs</a></li>
29+
<li><aclass="spec" href="/getting_started/16.html">Protocols</a></li>
30+
<li><aclass="spec" href="/getting_started/17.html">try, catch and rescue</a></li>
31+
<li><aclass="spec" href="/getting_started/18.html">Comprehensions</a></li>
32+
<li><aclass="spec" href="/getting_started/19.html">Sigils</a></li>
33+
<li><aclass="spec" href="/getting_started/20.html">Quote and unquote</a></li>
3434
<li><aclass="spec" href="/getting_started/21.html">Macros</a></li>
3535
</ol>
3636
</div>

‎getting_started/10.markdown‎

Lines changed: 139 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,177 @@
11
---
22
layout: getting_started
3-
title: 10 Recursion
3+
title: 10 alias, require and import
44
guide: 10
55
---
66

77
# {{page.title }}
88

9-
Due to immutability, loops in Elixir (and in functional programming languages) are written differently from conventional imperative languages. For example, in an imperative language, one would write:
9+
In order to facilitate software reuse, Elixir provides three directives. As we are going to see below, they are called directives because they have **lexical scope**.
1010

11-
```c
12-
for(i = 0; i < array.length; i++){
13-
array[i] = array[i] * 2
14-
}
11+
### 10.1 alias
12+
13+
`alias` allows you to setup aliases for any given module name. Imagine our `Math` module uses a special list implementation for doing math specific operations:
14+
15+
```elixir
16+
defmoduleMathdo
17+
aliasMath.List, as:List
18+
end
19+
```
20+
21+
From now on, any reference to `List` will automatically expand to `Math.List`. In case one wants to access the original `List`, it can be done by accessing the module via `Elixir`:
22+
23+
```elixir
24+
List.flatten#=> uses Math.List.flatten
25+
Elixir.List.flatten#=> uses List.flatten
26+
Elixir.Math.List.flatten#=> uses Math.List.flatten
27+
```
28+
29+
> Note: All modules defined in Elixir are defined inside a main Elixir namespace. However, for convenience, you can omit "Elixir." when referencing them.
30+
31+
Aliases are frequently used to define shortcuts. In fact, calling `alias` without an `as` option sets the alias automatically to the last part of the module name, for example:
32+
33+
```elixir
34+
aliasMath.List
1535
```
1636

17-
In the example above we are mutating the array which is not possible in Elixir. Instead functional languages rely on recursion: a function is called recursively until a condition is reached. Consider the example below that manually sums all the items in the list:
37+
Is the same as:
38+
39+
```elixir
40+
aliasMath.List, as:List
41+
```
42+
43+
Note that `alias` is **lexically scoped**, which allows you to set aliases inside specific functions:
1844

1945
```elixir
2046
defmoduleMathdo
21-
defsum_list([h|t], acc) do
22-
sum_list(t, h + acc)
47+
defplus(a, b) do
48+
aliasMath.List
49+
# ...
2350
end
2451

25-
defsum_list([], acc) do
26-
acc
52+
defminus(a, b) do
53+
# ...
2754
end
2855
end
56+
```
57+
58+
In the example above, since we are invoking `alias` inside the function `plus/2`, the alias will just be valid inside the function `plus/2`. `minus/2` won't be affected at all.
2959

30-
Math.sum_list([1, 2, 3], 0) #=> 6
60+
### 10.2 require
61+
62+
Elixir provides macros as a mechanism for meta-programming (writing code that generates code).
63+
64+
Macros are chunks of code that are executed and expanded at compilation time. This means, in order to use a macro, we need to guarantee its module and implementation are available during compilation. This is done with the `require` directive:
65+
66+
```iex
67+
iex> Integer.odd?(3)
68+
** (CompileError) iex:1: you must require Integer before invoking the macro Integer.odd?/1
69+
iex> require Integer
70+
nil
71+
iex> Integer.odd?(3)
72+
true
73+
```
74+
75+
In Elixir, `Integer.odd?/1` is defined as a macro so it can be used as guards. This means that, in order to invoke `Integer.odd?/1`, we need to first require the `Integer` module.
76+
77+
In general a module does not need to be required before usage, except if we want to use the macros available in that module. An attempt to call a macro that was not loaded will raise an error. Note that like the `alias` directive, `require` is also lexically scoped. We will talk more about macros in a later chapter.
78+
79+
### 10.3 import
80+
81+
We use `import` whenever we want to easily access functions or macros from other modules without using the qualified name. For instance, if we want to use the `duplicate` function from `List` several times, we can simply import it:
82+
83+
```iex
84+
iex> import List, only: [duplicate: 2]
85+
nil
86+
iex> duplicate :ok, 3
87+
[:ok, :ok, :ok]
3188
```
3289

33-
In the example above, we invoke `sum_list` giving a list `[1,2,3]` and the initial value `0` as arguments. When a function has many clauses, we will try each clause until we find one that matches according to the pattern matching rules. In this case, the list `[1,2,3]` matches against `[h|t]` which assigns `h = 1` and `t = [2,3]` while `acc` is set to `0`.
90+
In this case, we are importing only the function `duplicate` (with arity 2) from `List`. Although `only:` is optional, its usage is recommended. `except` could also be given as an option.
3491

35-
Then, we add the head of the list to the accumulator `h + acc` and call `sum_list` again, recursively, passing the tail of the list as argument. The tail will once again match `[h|t]` until the list is empty, as seen below:
92+
`import` also supports `:macros` and `:functions` to be given to `:only`. For example, to import all macros, one could write:
3693

3794
```elixir
38-
sum_list [1, 2, 3], 0
39-
sum_list [2, 3], 1
40-
sum_list [3], 3
41-
sum_list [], 6
95+
importInteger, only::macros
4296
```
4397

44-
When the list is empty, it will match the final clause which returns the final result of `6`. In imperative languages, such implementation would usually fail for large lists because the stack (in which our execution path is kept) would grow until it reaches a limit. Elixir, however, does tail call optimization in which the stack does not grow when a function exits by calling another function.
98+
Or to import all functions, you could write:
4599

46-
Recursion and tail call optimization are an important part of Elixir and are commonly used to create loops, especially in cases where processes need to wait and respond to messages. However, recursion as above is rarely used to manipulate lists, since [the `Enum` module](/docs/stable/Enum.html) already provides many conveniences for working not only with lists but different kinds of Enumerables. For instance, the example above could be simply written as:
100+
```elixir
101+
importInteger, only::functions
102+
```
103+
104+
Note that `import` is also **lexically scoped**, this means we can import specific macros inside specific functions:
105+
106+
```elixir
107+
defmoduleMathdo
108+
defsome_functiondo
109+
importList, only: [duplicate:2]
110+
# call duplicate
111+
end
112+
end
113+
```
114+
115+
In the example above, the imported `List.duplicate/2` is only visible within that specific function. `duplicate/2` won't be available in any other function in that module (or any other module for that matter).
116+
117+
Note that importing a module automatically requires it.
118+
119+
## 10.4 Aliases
120+
121+
At this point, you may be wondering, what exactly an Elixir alias is and how it is represented?
122+
123+
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"`:
47124

48125
```iex
49-
iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
50-
6
126+
iex> is_atom(String)
127+
true
128+
iex> to_string(String)
129+
"Elixir.String"
130+
iex> :"Elixir.String"
131+
String
51132
```
52133

53-
Or using the capture syntax:
134+
By using the `alias/2` directive, we are simply changing what an alias translates to.
135+
136+
Aliases work as described because in the Erlang VM (and consequently Elixir), modules are represented by atoms. For example, that's the mechanism we use to call Erlang modules:
54137

55138
```iex
56-
iex> Enum.reduce([1, 2, 3], 0, &+/2)
57-
6
139+
iex> :lists.flatten([1,[2],3])
140+
[1, 2, 3]
58141
```
59142

60-
Or finally the `sum/1`function:
143+
This is also the mechanism that allows us to dynamically call a given function in a module:
61144

62145
```iex
63-
iex> Enum.sum([1, 2, 3])
64-
6
146+
iex> mod = :lists
147+
:lists
148+
iex> mod.flatten([1,[2],3])
149+
[1,2,3]
150+
```
151+
152+
In other words, we are simply calling the function `flatten` on the atom `:lists`.
153+
154+
## 10.5 Nesting
155+
156+
Now that we have talked about aliases, we can talk about nesting and how it works in Elixir. Consider the following example:
157+
158+
```elixir
159+
defmoduleFoodo
160+
defmoduleBardo
161+
end
162+
end
163+
```
164+
165+
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 developer decides to move `Bar` to another file, it will need to be referenced by its full name (`Foo.Bar`) or an alias needs to be set using the `alias` directive discussed above.
166+
167+
In other words, the code above is exactly the same as:
168+
169+
```elixir
170+
defmoduleElixir.Foodo
171+
defmoduleElixir.Foo.Bardo
172+
end
173+
aliasElixir.Foo.Bar, as:Bar
174+
end
65175
```
66176

67-
We will learn more about Enumerables in the "Enumerables and streams" chapter.
177+
As we will see in later chapters, aliases also play a crucial role in macros, to guarantee they are hygienic. With this we are almost finishing our tour about Elixir modules, the last topic to cover is module attributes.

0 commit comments

Comments
(0)