You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the example above, we are mutating the array which is not possible in Elixir. Therefore, 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:
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:
18
18
19
19
```elixir
20
20
defmoduleMathdo
@@ -27,16 +27,16 @@ defmodule Math do
27
27
end
28
28
end
29
29
30
-
Math.sum_list([1,2,3], 0) #=> 6
30
+
Math.sum_list([1,2, 3], 0) #=> 6
31
31
```
32
32
33
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`.
34
34
35
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:
36
36
37
37
```elixir
38
-
sum_list [1,2,3], 0
39
-
sum_list [2,3], 1
38
+
sum_list [1,2, 3], 0
39
+
sum_list [2,3], 1
40
40
sum_list [3], 3
41
41
sum_list [], 6
42
42
```
@@ -46,14 +46,21 @@ When the list is empty, it will match the final clause which returns the final r
46
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:
47
47
48
48
```iex
49
-
iex> Enum.reduce([1,2,3], 0, fn(x, acc) -> x + acc end)
Copy file name to clipboardExpand all lines: getting_started/11.markdown
+10-19Lines changed: 10 additions & 19 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -21,23 +21,14 @@ end
21
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`:
> Note: All modules defined in Elixir are defined inside a main Elixir namespace. However, for convenience, you can omit the Elixir main namespace.
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
30
31
-
Aliases are frequently used to define shortcuts:
32
-
33
-
```iex
34
-
iex> alias String, as: S
35
-
nil
36
-
iex> S.length "hello"
37
-
5
38
-
```
39
-
40
-
Calling `alias` without an `as` option sets the alias automatically to the last part of the module name, for example:
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:
41
32
42
33
```elixir
43
34
aliasMath.List
@@ -68,7 +59,7 @@ In the example above, since we are invoking `alias` inside the function `plus/2`
68
59
69
60
### 11.2 require
70
61
71
-
Elixir provides macros.
62
+
Elixir provides macros as a mechanism for meta-programming (writing code that generates code).
72
63
73
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:
74
65
@@ -83,7 +74,7 @@ true
83
74
84
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.
85
76
86
-
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.
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.
87
78
88
79
### 11.3 import
89
80
@@ -127,9 +118,9 @@ Note that importing a module automatically requires it.
127
118
128
119
## 11.4 Aliases
129
120
130
-
At this point, you may be wondering, what exactly an Elixir alias is an how it is represented?
121
+
At this point, you may be wondering, what exactly an Elixir alias is and how it is represented?
131
122
132
-
An alias in Elixir is a capitalized identifier (like `String`, `Keyword`, etc) which is converted to an atom representing a module during compilation. For instance, the `String` alias translates by default to the atom `:"Elixir.String"`:
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"`:
133
124
134
125
```iex
135
126
iex> is_atom(String)
@@ -183,4 +174,4 @@ defmodule Elixir.Foo do
183
174
end
184
175
```
185
176
186
-
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.
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.
Copy file name to clipboardExpand all lines: getting_started/12.markdown
+5-5Lines changed: 5 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -99,7 +99,7 @@ end
99
99
100
100
> Note: Unlike Erlang, user defined attributes are not stored in the module by default. The value exists only during compilation time. A developer can configure an attribute to behave closer to Erlang by calling [`Module.register_attribute/3`](/docs/stable/Module.html#register_attribute/3).
101
101
102
-
Trying to access an attribute that was not previously defined will raise a warning:
102
+
Trying to access an attribute that was not defined will print a warning:
103
103
104
104
```elixir
105
105
defmoduleMyServerdo
@@ -150,9 +150,9 @@ IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
150
150
Plug.Adapters.Cowboy.httpMyPlug, []
151
151
```
152
152
153
-
In the example above, we have used the `plug/1` macro to connect functions that will be invoked when there is a web request. Internally, every time you call `plug/1`, the Plug library stores the given arguments as a list in a `@plugs` attribute. At the end, before the module is compiled, Plug runs a callback (registered via the `@before_compile` module attribute) that will then compile all `@plugs` into actual function calls.
153
+
In the example above, we have used the `plug/1` macro to connect functions that will be invoked when there is a web request. Internally, every time you call `plug/1`, the Plug library stores the given arguments as a list in a `@plugs` attribute. At the end, before the module is compiled, Plug runs a callback that will then compile all `@plugs` into actual function calls.
154
154
155
-
In order to understand the underlying code, we'd need macros, so we will revisit this pattern later on. However, the focus here is exactly on how using module attributes as storage allow developers to create DSLs. Note only they are elegant DSLs but using them with a combination of `@before_compile` callbacks allows developers to generate code that performs well.
155
+
In order to understand the underlying code, we'd need macros, so we will revisit this pattern later on. However, the focus here is exactly on how using module attributes as storage allow developers to create DSLs.
156
156
157
157
Another example comes from the ExUnit framework, that uses module attributes as annotation and storage:
158
158
@@ -167,6 +167,6 @@ defmodule MyTest do
167
167
end
168
168
```
169
169
170
-
Tags in ExUnit are used to annotate tests. Tags can be later used to filter tests, so you don't run external tests by default locally, since they can be slow, while you can leave them enabled in your builds. Internally, those tags are stored in temporary module attributes, associating each test to their respective tags, so it can be later fetched and filtered by the test framework.
170
+
Tags in ExUnit are used to annotate tests. Tags can be later used to filter tests, so you don't run external tests by default locally unless explicitly desired. Internally, those tags are stored in temporary module attributes, associating each test to their respective tags, so it can be later fetched and filtered by the test framework.
171
171
172
-
We hope this section shows some light on how Elixir supports meta-programming and how module attributes play an important role when doing so. In the next chapters, we will talk about some common modules in Elixir, like `IO` and `Enum`, before finally going into more advanced topics, like macros.
172
+
We hope this section shines some light on how Elixir supports meta-programming and how module attributes play an important role when doing so. In the next chapters, we will talk about some common modules in Elixir, like `IO` and `Enum`, before finally going into more advanced topics, like macros.
Copy file name to clipboardExpand all lines: getting_started/13.markdown
+10-10Lines changed: 10 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,9 +28,9 @@ iex> Enum.reduce(1..3, 0, &+/2)
28
28
6
29
29
```
30
30
31
-
Since the Enum module was designed to work across different data types, its API is limited to functions that are useful across many data types. For specific operations, you may need to reach to modules specific to the data types. For example, if you want to insert an element at a given position in a list, you should use the `List.insert_at/3` function from [the `List` module](/docs/stable/List.html), as it would make little sense to insert a value into, for example, a range.
31
+
Since the Enum module was designed to work across different data types, its API is limited to functions that are useful across many data types. For specific operations, you may need to reach to modules specific to the data types. For example, if you want to insert an element at a given position in a list, you should use the `List.insert_at/3` function from [the `List` module](/docs/stable/List.html), as it would make little sense to insert a value into, for example, a range.
32
32
33
-
We say the functions in the `Enum` module are polymorphic because they can work with diverse data types. In particular, the functions in the `Enum` module can work with any data type that implements [the `Enumerable` protocol](/docs/stable/Enumerable.html). We are going to discuss Protocols in a later chapter, now we are going to move on to a specific kind of enumerable called streams.
33
+
We say the functions in the `Enum` module are polymorphic because they can work with diverse data types. In particular, the functions in the `Enum` module can work with any data type that implements [the `Enumerable` protocol](/docs/stable/Enumerable.html). We are going to discuss Protocols in a later chapter, for now we are going to move on to a specific kind of enumerable called streams.
The example above has a pipeline of operations. We start with a range and then multiply each element in the range by 3. This first operation will now create and return a list with `100_000` items. Then we retrieve keep all odd elements from the list, generating a new list, now with `50_000` items, and then we sum all entries.
53
+
The example above has a pipeline of operations. We start with a range and then multiply each element in the range by 3. This first operation will now create and return a list with `100_000` items. Then we keep all odd elements from the list, generating a new list, now with `50_000` items, and then we sum all entries.
54
54
55
55
As an alternative, Elixir provides [the `Stream` module](/docs/stable/Stream.html) which supports lazy operations:
Many functions in the `Stream` module accept any enumerable as argument and return a stream as result. It also provides functions for creating streams, possibly infinite. For example, `Stream.cycle/1` can be used to create a stream that cycles around a given enumerable. Be careful to not call a function like `Enum.map/2` on such streams, as they would cycle forever:
82
+
Many functions in the `Stream` module accept any enumerable as argument and return a stream as result. It also provides functions for creating streams, possibly infinite. For example, `Stream.cycle/1` can be used to create a stream that cycles a given enumerable infinitely. Be careful to not call a function like `Enum.map/2` on such streams, as they would cycle forever:
83
83
84
84
```iex
85
85
iex> stream = Stream.cycle([1, 2, 3])
@@ -97,7 +97,7 @@ On the other hand, `Stream.unfold/2` can be used to generate values from a given
97
97
["h", "e", "ł"]
98
98
```
99
99
100
-
Another interesting function is `Stream.resource/3`, which can be used to wrap around resources, guaranteeing they are opened right before enumeration and closed afterwards, even in case of failures. For example, we can use it to stream a file:
100
+
Another interesting function is `Stream.resource/3` which can be used to wrap around resources, guaranteeing they are opened right before enumeration and closed afterwards, even in case of failures. For example, we can use it to stream a file:
101
101
102
102
```iex
103
103
iex> stream = File.stream!("path/to/file")
@@ -107,4 +107,4 @@ iex> Enum.take(stream, 10)
107
107
108
108
The example above will fetch the first 10 lines of the file you have selected. This means streams can be very useful for handling large files or even slow resources like network resources.
109
109
110
-
The amount of functions and functionality in [`Enum`](/docs/stable/Enum.html) and [`Stream`](/docs/stable/Stream.html) modules can be daunting at first, but you will get familiar with them case by case when needed. In particular, focus on the `Enum` module first and only move to `Stream` for the particular scenarios where laziness is required to either deal with slow resources or infinite collections.
110
+
The amount of functions and functionality in [`Enum`](/docs/stable/Enum.html) and [`Stream`](/docs/stable/Stream.html) modules can be daunting at first but you will get familiar with them case by case. In particular, focus on the `Enum` module first and only move to `Stream` for the particular scenarios where laziness is required to either deal with slow resources or large, possibly infinite, collections.
Copy file name to clipboardExpand all lines: getting_started/14.markdown
+2-2Lines changed: 2 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,7 +26,7 @@ iex> Process.alive?(pid)
26
26
false
27
27
```
28
28
29
-
> Note: you will likely get different process identifiers than the ones we are getting in the tutorial.
29
+
> Note: you will likely get different process identifiers than the ones we are getting in this guide.
30
30
31
31
We can retrieve the PID of the current process by calling `self/0`:
32
32
@@ -123,6 +123,6 @@ end
123
123
124
124
This time the process failed and brought the parent process down as they are linked. Linking can also be done manually by calling `Process.link/2`. We recommend you to take a look at [the `Process` module](/docs/stable/Process.html) for other functionality provided by processes.
125
125
126
-
Process and links play an important role when building fault-tolerant systems. In Elixir applications, we often link our processes to supervisors which will detect when a process die and start a new process in its place.
126
+
Process and links play an important role when building fault-tolerant systems. In Elixir applications we often link our processes to supervisors which will detect when a process die and start a new process in its place. This is only possible because processes don't share anything by default so there is no way the crash of a process can corrupt the state of another process!
127
127
128
128
While other languages would require us to catch/handle exceptions, in Elixir we are actually fine with letting process fail because we expect supervisors to properly restart our systems. "Failing fast" is a common philosophy when writing Elixir software!
0 commit comments