Skip to content

Commit 8b596d2

Browse files
committed
Rename chapter 12 and fix a bunch of errors in that chapter
Chapter 12 was called just "IO" but explained both how to do IO in Elixir as well as how to interact with files and the file system (the File and Path modules). The new title - "IO and the file system" - reflects exactly that.
1 parent 7a7fc34 commit 8b596d2

File tree

2 files changed

+34
-39
lines changed

2 files changed

+34
-39
lines changed

_layouts/getting_started.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ <h3 class="widget-title">Getting Started</h3>
2121
<li><a class="spec" href="/getting_started/9.html">Recursion</a></li>
2222
<li><a class="spec" href="/getting_started/10.html">Enumerables and streams</a></li>
2323
<li><a class="spec" href="/getting_started/11.html">Processes</a></li>
24-
<li><a class="spec" href="/getting_started/12.html">IO</a></li>
24+
<li><a class="spec" href="/getting_started/12.html">IO and the file system</a></li>
2525
<li><a class="spec" href="/getting_started/13.html">alias, require and import</a></li>
2626
<li><a class="spec" href="/getting_started/14.html">Module attributes</a></li>
2727
<li><a class="spec" href="/getting_started/15.html">Structs</a></li>

getting_started/12.markdown

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
11
---
22
layout: getting_started
3-
title: 12 IO
3+
title: 12 IO and the file system
44
guide: 12
55
---
66

77
# {{ page.title }}
88

99
{% include toc.html %}
1010

11-
This chapter is a quick introduction to input/output mechanisms in Elixir and related modules, like [`IO`](/docs/stable/elixir/IO.html), [`File`](/docs/stable/elixir/File.html) and [`Path`](/docs/stable/elixir/Path.html).
11+
This chapter is a quick introduction to input/output mechanisms and file-system-related tasks, as well as to related modules like [`IO`](/docs/stable/elixir/IO.html), [`File`](/docs/stable/elixir/File.html) and [`Path`](/docs/stable/elixir/Path.html).
1212

1313
We had originally sketched this chapter to come much earlier in the getting started guide. However, we noticed the IO system provides a great opportunity to shed some light on some philosophies and curiosities of Elixir and the VM.
1414

15-
## 12.1 The IO module
15+
## 12.1 The `IO` module
1616

17-
The `IO` module in Elixir is the main mechanism for reading and writing to the standard io (`:stdio`), standard error (`:stderr`), files and other IO devices. Usage of the module is pretty straight-forward:
17+
The `IO` module is the main mechanism in Elixir for reading and writing to standard input/output (`:stdio`), standard error (`:stderr`), files and other IO devices. Usage of the module is pretty straightforward:
1818

1919
```iex
2020
iex> IO.puts "hello world"
@@ -25,17 +25,17 @@ yes or no? yes
2525
"yes\n"
2626
```
2727

28-
By default, the functions in the IO module use the standard input and output. We can pass the `:stderr` as argument to write to the standard error device:
28+
By default, functions in the IO module read from the standard input and write to the standard output. We can change that by passing, for example, `:stderr` as an argument (in order to write to the standard error device):
2929

3030
```iex
3131
iex> IO.puts :stderr, "hello world"
3232
hello world
3333
:ok
3434
```
3535

36-
## 12.2 The File module
36+
## 12.2 The `File` module
3737

38-
The [`File`](/docs/stable/elixir/File.html) module contains functions that allows us to open files as IO devices. By default, files are opened in binary mode, which requires developers to use the specific `IO.binread/2` and `IO.binwrite/2` functions from the `IO` module:
38+
The [`File`](/docs/stable/elixir/File.html) module contains functions that allow us to open files as IO devices. By default, files are opened in binary mode, which requires developers to use the specific `IO.binread/2` and `IO.binwrite/2` functions from the `IO` module:
3939

4040
```iex
4141
iex> {:ok, file} = File.open "hello", [:write]
@@ -48,16 +48,11 @@ iex> File.read "hello"
4848
{:ok, "world"}
4949
```
5050

51-
A file can also be opened with `:utf8` encoding which allows the remaining functions in the `IO` module to be used:
51+
A file can also be opened with `:utf8` encoding, which tells the `File` module to interpret the bytes read from the file as UTF-8-encoded bytes.
5252

53-
```iex
54-
iex> {:ok, file} = File.open "another", [:write, :utf8]
55-
{:ok, #PID<0.48.0>}
56-
```
57-
58-
Besides functions for opening, reading and writing files, the `File` module has many functions that work on the file system. Those functions are named after their UNIX equivalents. For example, `File.rm/1` can be used to remove files, `File.mkdir/1` to create directories, `File.mkdir_p/1` creates directories guaranteeing their parents exists and there is even `File.cp_r/2` and `File.rm_rf/2` which copy and remove files and directories recursively.
53+
Besides functions for opening, reading and writing files, the `File` module has many functions to work with the file system. Those functions are named after their UNIX equivalents. For example, `File.rm/1` can be used to remove files, `File.mkdir/1` to create directories, `File.mkdir_p/1` to create directories and all their parent chain. There are even `File.cp_r/2` and `File.rm_rf/2` to respectively copy and remove files and directories recursively (i.e., copying and removing the contents of the directories too).
5954

60-
You will also notice that functions in the `File` module have two variants, one with `!` (bang) in its name and others without. For example, when we read the "hello" file above, we have used the one without `!`. Let's try some new examples:
55+
You will also notice that functions in the `File` module have two variants: one "regular" variant and another variant which has the same name as the regular version but with a trailing bang (`!`). For example, when we read the `"hello"` file in the example above, we use `File.read/1`. Alternatively, we can use `File.read!/1`:
6156

6257
```iex
6358
iex> File.read "hello"
@@ -70,30 +65,28 @@ iex> File.read! "unknown"
7065
** (File.Error) could not read file unknown: no such file or directory
7166
```
7267

73-
Notice that when the file does not exist, the version with `!` raises an error. That said, the version without `!` is preferred when you want to handle different outcomes with pattern matching. However, if you expect the file to be there, the bang variation is more useful as it raises a meaningful error message. That said, never write:
74-
75-
```elixir
76-
{:ok, body} = File.read(file)
77-
```
78-
79-
Instead write:
68+
Notice that when the file does not exist, the version with `!` raises an error. The version without `!` is preferred when you want to handle different outcomes using pattern matching:
8069

8170
```elixir
8271
case File.read(file) do
83-
{:ok, body} -> # handle ok
84-
{:error, r} -> # handle error
72+
{:ok, body} -> # do something with the `body`
73+
{:error, reason} -> # handle the error caused by `reason`
8574
end
8675
```
8776

88-
or
77+
However, if you expect the file to be there, the bang variation is more useful as it raises a meaningful error message. Avoid writing:
8978

9079
```elixir
91-
File.read!(file)
80+
{:ok, body} = File.read(file)
9281
```
9382

83+
as, in case of an error, `File.read/1` will return `{:error, reason}` and the pattern matching will fail. You will still get the desired result (a raised error), but the message will be about the pattern which doesn't match (thus being cryptic in respect to what the error actually is about).
84+
85+
If you don't want to handle a possible error (i.e., you want it to bubble up), prefer using `File.read!/1`.
86+
9487
## 12.3 The Path module
9588

96-
The majority of the functions in the File module expects paths as arguments. Most commonly, those paths will be binaries and they can be manipulated with the [`Path`](/docs/stable/elixir/Path.html) module:
89+
The majority of the functions in the `File` module expect paths as arguments. Most commonly, those paths will be regular binaries. The [`Path`](/docs/stable/elixir/Path.html) module provides facilities for working with such paths:
9790

9891
```iex
9992
iex> Path.join("foo", "bar")
@@ -102,18 +95,20 @@ iex> Path.expand("~/hello")
10295
"/Users/jose/hello"
10396
```
10497

105-
With this we have covered the main modules for doing IO and interacting with the file system. Next we will discuss some curiosities and advanced topics regarding IO. Those sections are not necessary to write Elixir code, so feel free to skip them, but they do provide an overview of how the IO system is implemented in the VM and other curiosities.
98+
Using functions from the `Path` module as opposed to just manipulating binaries is preferred since the `Path` module takes care of different operating systems transparently. For example, `Path.join/2` joins a path with slashes (`/`) on Unix-like systems and with backslashes (`\\`) on Windows.
99+
100+
With this we have covered the main modules that Elixir provides for dealing with IO and interacting with the file system. In the next sections, we will discuss some advanced topics regarding IO. Those sections are not necessary in order to write Elixir code, so feel free to skip them, but they do provide a nice overview of how the IO system is implemented in the VM and other curiosities.
106101

107102
## 12.4 Processes and group leaders
108103

109-
You may have noticed that `File.open/2` returned a tuple containing a PID:
104+
You may have noticed that `File.open/2` returns a tuple like `{:ok, pid}`:
110105

111106
```iex
112107
iex> {:ok, file} = File.open "hello", [:write]
113108
{:ok, #PID<0.47.0>}
114109
```
115110

116-
That's because the IO module actually works with processes. When you say `IO.write(pid, binary)`, the IO module will send a message to the process with the desired operation. Let's see what happens if we use our own process:
111+
That happens because the `IO` module actually works with processes (see [chapter 11](/getting_started/11.html)). When you write `IO.write(pid, binary)`, the `IO` module will send a message to the process identified by `pid` with the desired operation. Let's see what happens if we use our own process:
117112

118113
```iex
119114
iex> pid = spawn fn ->
@@ -125,9 +120,9 @@ iex> IO.write(pid, "hello")
125120
** (ErlangError) erlang error: :terminated
126121
```
127122

128-
After `IO.write/2`, we can see the request sent by the IO module printed, which then fails since the IO module expected some kind of result that we did not supply.
123+
After `IO.write/2`, we can see the request sent by the `IO` module (a four-elements tuple) printed out. Soon after that, we see that it fails since the `IO` module expected some kind of result that we did not supply.
129124

130-
The [`StringIO`](/docs/stable/elixir/StringIO.html) module provides an implementation of the IO device messages on top of a string:
125+
The [`StringIO`](/docs/stable/elixir/StringIO.html) module provides an implementation of the `IO` device messages on top of strings:
131126

132127
```iex
133128
iex> {:ok, pid} = StringIO.open("hello")
@@ -136,9 +131,9 @@ iex> IO.read(pid, 2)
136131
"he"
137132
```
138133

139-
By modelling IO devices with processes, the Erlang VM allows different nodes in the same network to exchange file processes to read/write files in between nodes. Of all IO devices, there is one that is special to each process, called group leader.
134+
By modelling IO devices with processes, the Erlang VM allows different nodes in the same network to exchange file processes in order to read/write files in between nodes. Of all IO devices, there is one that is special to each process: the **group leader**.
140135

141-
When you write to `:stdio`, you are actually sending a message to the group leader, which writes to STDIO file descriptor:
136+
When you write to `:stdio`, you are actually sending a message to the group leader, which writes to the standard-input file descriptor:
142137

143138
```iex
144139
iex> IO.puts :stdio, "hello"
@@ -153,7 +148,7 @@ The group leader can be configured per process and is used in different situatio
153148

154149
## 12.5 `iodata` and `chardata`
155150

156-
In all examples above, we have used binaries/strings when writing to files. In the chapter "Binaries, strings and char lists", we mentioned how strings are simply bytes while char lists are lists with code points.
151+
In all of the examples above, we used binaries when writing to files. In the chapter ["Binaries, strings and char lists"](/getting_started/6.html), we mentioned how strings are simply bytes while char lists are lists with code points.
157152

158153
The functions in `IO` and `File` also allow lists to be given as arguments. Not only that, they also allow a mixed list of lists, integers and binaries to be given:
159154

@@ -166,10 +161,10 @@ hello world
166161
:ok
167162
```
168163

169-
However, this requires some attention. A list may represent either a bunch of bytes or a bunch of characters and which one to use depends on the encoding of the IO device. If the file is opened without encoding, the file is expected to be in raw mode, and the functions in the `IO` module starting with `bin*` must be used. Those functions expect an `iodata` as argument, i.e. it expects a list of integers representing bytes and binaries to be given.
164+
However, this requires some attention. A list may represent either a bunch of bytes or a bunch of characters and which one to use depends on the encoding of the IO device. If the file is opened without encoding, the file is expected to be in raw mode, and the functions in the `IO` module starting with `bin*` must be used. Those functions expect an `iodata` as argument; i.e., they expect a list of integers representing bytes and binaries to be given.
170165

171-
On the other hand, `:stdio` and files opened with `:utf8` encoding work with the remaining functions in the `IO` module and those expect a `char_data` as argument, i.e. they expect a list of characters or strings to be given.
166+
On the other hand, `:stdio` and files opened with `:utf8` encoding work with the remaining functions in the `IO` module. Those functions expect a `char_data` as argument, that is, a list of characters or strings.
172167

173168
Although this is a subtle difference, you only need to worry about those details if you intend to pass lists to those functions. Binaries are already represented by the underlying bytes and as such their representation is always raw.
174169

175-
This finishes our tour of IO devices and IO related functionality. We have learned about four Elixir modules, [`IO`](/docs/stable/elixir/IO.html), [`File`](/docs/stable/elixir/File.html), [`Path`](/docs/stable/elixir/Path.html) and [`StringIO`](/docs/stable/elixir/StringIO.html), as well as how the VM uses processes for the underlying IO mechanisms and how to use (char and io) data for IO operations.
170+
This finishes our tour of IO devices and IO related functionality. We have learned about four Elixir modules - [`IO`](/docs/stable/elixir/IO.html), [`File`](/docs/stable/elixir/File.html), [`Path`](/docs/stable/elixir/Path.html) and [`StringIO`](/docs/stable/elixir/StringIO.html) - as well as how the VM uses processes for the underlying IO mechanisms and how to use `chardata` and `iodata` for IO operations.

0 commit comments

Comments
 (0)