Skip to content

Commit 0c83aea

Browse files
author
José Valim
committed
Diverse improvements to current chapters
1 parent dad597a commit 0c83aea

File tree

13 files changed

+128
-102
lines changed

13 files changed

+128
-102
lines changed

‎getting_started/10.markdown‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ for(i = 0; i < array.length; i++){
1414
}
1515
```
1616

17-
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:
1818

1919
```elixir
2020
defmoduleMathdo
@@ -27,16 +27,16 @@ defmodule Math do
2727
end
2828
end
2929

30-
Math.sum_list([1,2,3], 0) #=> 6
30+
Math.sum_list([1,2, 3], 0) #=> 6
3131
```
3232

3333
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`.
3434

3535
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:
3636

3737
```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
4040
sum_list [3], 3
4141
sum_list [], 6
4242
```
@@ -46,14 +46,21 @@ When the list is empty, it will match the final clause which returns the final r
4646
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:
4747

4848
```iex
49-
iex> Enum.reduce([1,2,3], 0, fn(x, acc) -> x + acc end)
49+
iex> Enum.reduce([1, 2, 3], 0, fn(x, acc) -> x + acc end)
5050
6
5151
```
5252

5353
Or using the capture syntax:
5454

5555
```iex
56-
iex> Enum.reduce([1,2,3], 0, &+/2)
56+
iex> Enum.reduce([1, 2, 3], 0, &+/2)
57+
6
58+
```
59+
60+
Or finally the `sum/1` function:
61+
62+
```iex
63+
iex> Enum.sum([1, 2, 3])
5764
6
5865
```
5966

‎getting_started/11.markdown‎

Lines changed: 10 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,14 @@ end
2121
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`:
2222

2323
```elixir
24-
List.values#=> uses Math.List.values
25-
Elixir.List.values#=> uses List.values
26-
Elixir.Math.List.values#=> uses Math.List.values
24+
List.flatten#=> uses Math.List.flatten
25+
Elixir.List.flatten#=> uses List.flatten
26+
Elixir.Math.List.flatten#=> uses Math.List.flatten
2727
```
2828

29-
> 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.
3030
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:
4132

4233
```elixir
4334
aliasMath.List
@@ -68,7 +59,7 @@ In the example above, since we are invoking `alias` inside the function `plus/2`
6859

6960
### 11.2 require
7061

71-
Elixir provides macros.
62+
Elixir provides macros as a mechanism for meta-programming (writing code that generates code).
7263

7364
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:
7465

@@ -83,7 +74,7 @@ true
8374

8475
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.
8576

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.
8778

8879
### 11.3 import
8980

@@ -127,9 +118,9 @@ Note that importing a module automatically requires it.
127118

128119
## 11.4 Aliases
129120

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?
131122

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"`:
133124

134125
```iex
135126
iex> is_atom(String)
@@ -183,4 +174,4 @@ defmodule Elixir.Foo do
183174
end
184175
```
185176

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.

‎getting_started/12.markdown‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ end
9999

100100
> 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).
101101
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:
103103

104104
```elixir
105105
defmoduleMyServerdo
@@ -150,9 +150,9 @@ IO.puts "Running MyPlug with Cowboy on http://localhost:4000"
150150
Plug.Adapters.Cowboy.httpMyPlug, []
151151
```
152152

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.
154154

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.
156156

157157
Another example comes from the ExUnit framework, that uses module attributes as annotation and storage:
158158

@@ -167,6 +167,6 @@ defmodule MyTest do
167167
end
168168
```
169169

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.
171171

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.

‎getting_started/13.markdown‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ iex> Enum.reduce(1..3, 0, &+/2)
2828
6
2929
```
3030

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.
3232

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.
3434

3535
## 13.2 Eager vs Lazy
3636

@@ -46,16 +46,16 @@ iex> Enum.filter(1..3, &Integer.odd?/1)
4646
This means that when performing multiple operations with `Enum`, each operation is going to generate an intermediate list until we reach the result:
4747

4848
```iex
49-
iex> 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(&Integer.odd?/1) |> Enum.reduce(&+/2)
49+
iex> 1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(&Integer.odd?/1) |> Enum.sum
5050
7500000000
5151
```
5252

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 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.
5454

5555
As an alternative, Elixir provides [the `Stream` module](/docs/stable/Stream.html) which supports lazy operations:
5656

5757
```iex
58-
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(&Integer.odd?/1) |> Enum.reduce(&+/2)
58+
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(&Integer.odd?/1) |> Enum.sum
5959
7500000000
6060
```
6161

@@ -69,17 +69,17 @@ They are lazy because, as shown in the example above, `1..100_000 |> Stream.map(
6969

7070
```iex
7171
iex> 1..100_000 |> Stream.map(&(&1 * 3))
72-
#Stream<1..100_000, chain: [#Function<34.16982430/1 in Stream.map/2>]>
72+
#Stream<1..100_000, funs: [#Function<34.16982430/1 in Stream.map/2>]>
7373
```
7474

7575
Furthermore, they are composable because we can pipe many stream operations:
7676

7777
```iex
7878
iex> 1..100_000 |> Stream.map(&(&1 * 3)) |> Stream.filter(&Integer.odd?/1)
79-
#Stream<1..100_000, chain: [...]>
79+
#Stream<1..100_000, funs: [...]>
8080
```
8181

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 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:
8383

8484
```iex
8585
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
9797
["h", "e", "ł"]
9898
```
9999

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:
101101

102102
```iex
103103
iex> stream = File.stream!("path/to/file")
@@ -107,4 +107,4 @@ iex> Enum.take(stream, 10)
107107

108108
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.
109109

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.

‎getting_started/14.markdown‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ iex> Process.alive?(pid)
2626
false
2727
```
2828

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.
3030
3131
We can retrieve the PID of the current process by calling `self/0`:
3232

@@ -123,6 +123,6 @@ end
123123

124124
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.
125125

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!
127127

128128
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

Comments
(0)