Skip to content

Commit 7291abb

Browse files
committed
Move the description of records to Record's moduledoc. Closes #1166
1 parent 55cb8ab commit 7291abb

File tree

2 files changed

+164
-154
lines changed

2 files changed

+164
-154
lines changed

lib/elixir/lib/kernel.ex

Lines changed: 15 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,155 +1362,16 @@ defmodule Kernel do
13621362
@doc %B"""
13631363
Defines a record.
13641364
1365-
A record is a tagged tuple which contains one or more elements
1366-
and the first element is a module. This macro defines a module
1367-
that generates accessors to manipulate the record at both
1368-
compilation and runtime.
1365+
This macro defines a module that generates accessors to manipulate the record
1366+
at both compilation and runtime.
1367+
1368+
See the `Record` module's documentation for a detailed description of records
1369+
in Elixir.
13691370
13701371
## Examples
13711372
13721373
defrecord FileInfo, atime: nil, accesses: 0
13731374
1374-
The line above will define a module named `FileInfo` which
1375-
contains a function named `new` that returns a new record
1376-
and other functions to read and set the values in the
1377-
record:
1378-
1379-
file_info = FileInfo.new(atime: now())
1380-
file_info.atime #=> Returns the value of atime
1381-
file_info.atime(now()) #=> Updates the value of atime
1382-
1383-
# Update multiple attributes at once:
1384-
file_info.update(atime: now(), accesses: 1)
1385-
1386-
# Obtain the keywords representation of a record:
1387-
file_info.to_keywords #=> [accesses: 1, atime: {1370,7171,911705}]
1388-
1389-
1390-
A record is simply a tuple where the first element is the record
1391-
module name. We can get the record raw representation as follow:
1392-
1393-
inspect FileInfo.new, raw: true
1394-
#=> { FileInfo, nil, nil }
1395-
1396-
Besides defining readers and writers for each attribute, Elixir also
1397-
defines an `update_#{attribute}` function to update the value. Such
1398-
functions expect a function as argument that receives the current
1399-
value and must return the new one. For example, every time the file
1400-
is accessed, the accesses counter can be incremented with:
1401-
1402-
file_info.update_accesses(fn(old) -> old + 1 end)
1403-
1404-
Which can be also written as:
1405-
1406-
file_info.update_accesses(&1 + 1)
1407-
1408-
## Access syntax
1409-
1410-
Records in Elixir can be expanded at compilation time to provide
1411-
pattern matching and faster operations. For example, the clause
1412-
below will only match if a `FileInfo` is given and the number of
1413-
accesses is zero:
1414-
1415-
def enforce_no_access(FileInfo[accesses: 0]), do: :ok
1416-
1417-
The clause above will expand to:
1418-
1419-
def enforce_no_access({ FileInfo, _, 0 }), do: :ok
1420-
1421-
The downside of using such syntax is that, every time the record
1422-
changes, your code now needs to be recompiled (which is usually
1423-
not a concern since Elixir build tools by default recompiles the
1424-
whole project whenever there is a change).
1425-
1426-
Finally, keep in mind that Elixir triggers some optimizations whenever
1427-
the access syntax is used. For example:
1428-
1429-
def no_access?(FileInfo[] = file_info) do
1430-
file_info.accesses == 0
1431-
end
1432-
1433-
Is translated to:
1434-
1435-
def no_access?({ FileInfo, _, _ } = file_info) do
1436-
elem(file_info, 1) == 0
1437-
end
1438-
1439-
Which provides faster get and set times for record operations.
1440-
1441-
## Runtime introspection
1442-
1443-
At runtime, developers can use `__record__` to get information
1444-
about the given record:
1445-
1446-
FileInfo.__record__(:name)
1447-
#=> FileInfo
1448-
1449-
FileInfo.__record__(:fields)
1450-
#=> [atime: nil, accesses: 0]
1451-
1452-
In order to quickly access the index of a field, one can use
1453-
the `__index__` function:
1454-
1455-
FileInfo.__index__(:atime)
1456-
#=> 0
1457-
1458-
FileInfo.__index__(:unknown)
1459-
#=> nil
1460-
1461-
## Compile-time introspection
1462-
1463-
At the compile time, one can access following information about the record
1464-
from within the record module:
1465-
1466-
* `@record_fields` — a keyword list of record fields with defaults
1467-
* `@record_types` — a keyword list of record fields with types
1468-
1469-
defrecord Foo, bar: nil do
1470-
record_type bar: nil | integer
1471-
IO.inspect @record_fields
1472-
IO.inspect @record_types
1473-
end
1474-
1475-
prints out
1476-
1477-
[bar: nil]
1478-
[bar: {:|,[line: ...],[nil,{:integer,[line: ...],nil}]}]
1479-
1480-
where the last line is a quoted representation of
1481-
1482-
[bar: nil | integer]
1483-
1484-
## Documentation
1485-
1486-
By default records are not documented and have `@moduledoc` set to false.
1487-
1488-
## Types
1489-
1490-
Every record defines a type named `t` that can be accessed in typespecs.
1491-
For example, assuming the `Config` record defined above, it could be used
1492-
in typespecs as follow:
1493-
1494-
@spec handle_config(Config.t) :: boolean()
1495-
1496-
Inside the record definition, a developer can define his own types too:
1497-
1498-
defrecord Config, counter: 0, failures: [] do
1499-
@type kind :: term
1500-
record_type counter: integer, failures: [kind]
1501-
end
1502-
1503-
When defining a type, all the fields not mentioned in the type are
1504-
assumed to have type `term`.
1505-
1506-
## Importing records
1507-
1508-
It is also possible to import a public record (a record, defined using
1509-
`defrecord`) as a set of private macros (as if it was defined using `defrecordp`):
1510-
1511-
Record.import Config, as: :config
1512-
1513-
See `Record.import/2` and `defrecordp/2` documentation for more information
15141375
"""
15151376

15161377
defmacro defrecord(name, fields, do_block // [])
@@ -1525,15 +1386,15 @@ defmodule Kernel do
15251386
@doc """
15261387
Defines a record with a set of private macros to manipulate it.
15271388
1528-
A record is a tagged tuple which contains one or more elements
1529-
and the first element is a module. This macro defines a set of
1530-
macros private to the current module to manipulate the record
1531-
exclusively at compilation time.
1389+
This macro defines a set of macros private to the current module to
1390+
manipulate the record exclusively at compilation time.
1391+
1392+
`defrecordp` must be used instead of `defrecord` when there is no interest in
1393+
exposing the record outside of the module it's defined in. In many ways, it
1394+
is similar to an Erlang record, since it is only available at compilation time.
15321395
1533-
`defrecordp` must be used instead of `defrecord` when there is
1534-
no interest in exposing the record as a whole. In many ways,
1535-
it is similar to Erlang records, since it is only available at
1536-
compilation time.
1396+
See the `Record` module's documentation for a detailed description of records
1397+
in Elixir.
15371398
15381399
## Examples
15391400
@@ -1578,8 +1439,8 @@ defmodule Kernel do
15781439
15791440
1) Differently from records, exceptions are documented by default;
15801441
1581-
2) Exceptions **must** implement `message/1` as API that return a
1582-
binary as result;
1442+
2) Exceptions **must** implement `message/1` -- a function that returns a
1443+
string;
15831444
15841445
"""
15851446
defmacro defexception(name, fields, opts // [], do_block // []) do

lib/elixir/lib/record.ex

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,155 @@
11
defmodule Record do
22
@moduledoc """
33
Functions to define Elixir records
4+
5+
A record is a tagged tuple which contains one or more elements and the first
6+
element is a module. One creates a record by calling `defrecord` or
7+
`defrecordp` which are documented in `Kernel`.
8+
9+
## Examples
10+
11+
defrecord FileInfo, atime: nil, accesses: 0
12+
13+
The line above will define a module named `FileInfo` which
14+
contains a function named `new` that returns a new record
15+
and other functions to read and set the values in the
16+
record:
17+
18+
file_info = FileInfo.new(atime: now())
19+
file_info.atime #=> Returns the value of atime
20+
file_info.atime(now()) #=> Updates the value of atime
21+
22+
# Update multiple attributes at once:
23+
file_info.update(atime: now(), accesses: 1)
24+
25+
# Obtain the keywords representation of a record:
26+
file_info.to_keywords #=> [accesses: 1, atime: {1370,7171,911705}]
27+
28+
29+
A record is simply a tuple where the first element is the record
30+
module name. We can get the record raw representation as follow:
31+
32+
inspect FileInfo.new, raw: true
33+
#=> { FileInfo, nil, nil }
34+
35+
Besides defining readers and writers for each attribute, Elixir also
36+
defines an `update_#{attribute}` function to update the value. Such
37+
functions expect a function as argument that receives the current
38+
value and must return the new one. For example, every time the file
39+
is accessed, the accesses counter can be incremented with:
40+
41+
file_info.update_accesses(fn(old) -> old + 1 end)
42+
43+
Which can be also written as:
44+
45+
file_info.update_accesses(&1 + 1)
46+
47+
## Access syntax
48+
49+
Records in Elixir can be expanded at compilation time to provide
50+
pattern matching and faster operations. For example, the clause
51+
below will only match if a `FileInfo` is given and the number of
52+
accesses is zero:
53+
54+
def enforce_no_access(FileInfo[accesses: 0]), do: :ok
55+
56+
The clause above will expand to:
57+
58+
def enforce_no_access({ FileInfo, _, 0 }), do: :ok
59+
60+
The downside of using such syntax is that, every time the record
61+
changes, your code now needs to be recompiled (which is usually
62+
not a concern since Elixir build tools by default recompiles the
63+
whole project whenever there is a change).
64+
65+
Finally, keep in mind that Elixir triggers some optimizations whenever
66+
the access syntax is used. For example:
67+
68+
def no_access?(FileInfo[] = file_info) do
69+
file_info.accesses == 0
70+
end
71+
72+
Is translated to:
73+
74+
def no_access?({ FileInfo, _, _ } = file_info) do
75+
elem(file_info, 1) == 0
76+
end
77+
78+
Which provides faster get and set times for record operations.
79+
80+
## Runtime introspection
81+
82+
At runtime, developers can use `__record__` to get information
83+
about the given record:
84+
85+
FileInfo.__record__(:name)
86+
#=> FileInfo
87+
88+
FileInfo.__record__(:fields)
89+
#=> [atime: nil, accesses: 0]
90+
91+
In order to quickly access the index of a field, one can use
92+
the `__index__` function:
93+
94+
FileInfo.__index__(:atime)
95+
#=> 0
96+
97+
FileInfo.__index__(:unknown)
98+
#=> nil
99+
100+
## Compile-time introspection
101+
102+
At the compile time, one can access following information about the record
103+
from within the record module:
104+
105+
* `@record_fields` — a keyword list of record fields with defaults
106+
* `@record_types` — a keyword list of record fields with types
107+
108+
defrecord Foo, bar: nil do
109+
record_type bar: nil | integer
110+
IO.inspect @record_fields
111+
IO.inspect @record_types
112+
end
113+
114+
prints out
115+
116+
[bar: nil]
117+
[bar: {:|,[line: ...],[nil,{:integer,[line: ...],nil}]}]
118+
119+
where the last line is a quoted representation of
120+
121+
[bar: nil | integer]
122+
123+
## Documentation
124+
125+
By default records are not documented and have `@moduledoc` set to false.
126+
127+
## Types
128+
129+
Every record defines a type named `t` that can be accessed in typespecs.
130+
For example, assuming the `Config` record defined above, it could be used
131+
in typespecs as follow:
132+
133+
@spec handle_config(Config.t) :: boolean()
134+
135+
Inside the record definition, a developer can define his own types too:
136+
137+
defrecord Config, counter: 0, failures: [] do
138+
@type kind :: term
139+
record_type counter: integer, failures: [kind]
140+
end
141+
142+
When defining a type, all the fields not mentioned in the type are
143+
assumed to have type `term`.
144+
145+
## Importing records
146+
147+
It is also possible to import a public record (a record, defined using
148+
`defrecord`) as a set of private macros (as if it was defined using `defrecordp`):
149+
150+
Record.import Config, as: :config
151+
152+
See `Record.import/2` and `defrecordp/2` documentation for more information
4153
"""
5154

6155
@doc """

0 commit comments

Comments
 (0)