1
<?xml version="1.0" encoding="latin1" ?>
2
<!DOCTYPE chapter SYSTEM "chapter.dtd">
7
<year>2003</year><year>2009</year>
8
<holder>Ericsson AB. All Rights Reserved.</holder>
11
The contents of this file are subject to the Erlang Public License,
12
Version 1.1, (the "License"); you may not use this file except in
13
compliance with the License. You should have received a copy of the
14
Erlang Public License along with this software. If not, it can be
15
retrieved online at http://www.erlang.org/.
17
Software distributed under the License is distributed on an "AS IS"
18
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
19
the License for the specific language governing rights and limitations
24
<title>Records</title>
29
<file>records.xml</file>
33
<title>Records vs Tuples</title>
34
<p>The main advantage of using records instead of tuples is that
35
fields in a record are accessed by name, whereas fields in a
36
tuple are accessed by position. To illustrate these differences,
37
suppose that we want to represent a person with the tuple
38
<c>{Name, Address, Phone}</c>.</p>
39
<p>We must remember that the <c>Name</c> field is the first
40
element of the tuple, the <c>Address</c> field is the second
41
element, and so on, in order to write functions which manipulate
42
this data. For example, to extract data from a variable <c>P</c>
43
which contains such a tuple we might write the following code
44
and then use pattern matching to extract the relevant fields.</p>
47
Address = element(2, P),
49
<p>Code like this is difficult to read and understand and errors
50
occur if we get the numbering of the elements in the tuple wrong.
51
If we change the data representation by re-ordering the fields,
52
or by adding or removing a field, then all references to
53
the person tuple, wherever they occur, must be checked and
54
possibly modified.</p>
55
<p>Records allow us to refer to the fields by name and not
56
position. We use a record instead of a tuple to store the data.
57
If we write a record definition of the type shown below, we can
58
then refer to the fields of the record by name.</p>
60
-record(person, {name, phone, address}).</code>
61
<p>For example, if <c>P</c> is now a variable whose value is a
62
<c>person</c> record, we can code as follows in order to access
63
the name and address fields of the records.</p>
66
Address = P#person.address,
68
<p>Internally, records are represented using tagged tuples:</p>
70
{person, Name, Phone, Address}</code>
74
<title>Defining a Record</title>
75
<p>This definition of a person will be used in many of
76
the examples which follow. It contains three fields, <c>name</c>,
77
<c>phone</c> and <c>address</c>. The default values for
78
<c>name</c> and <c>phone</c> is "" and [], respectively.
79
The default value for <c>address</c> is the atom
80
<c>undefined</c>, since no default value is supplied for this
83
-record(person, {name = "", phone = [], address}).</pre>
84
<p>We have to define the record in the shell in order to be able
85
use the record syntax in the examples:</p>
87
> <input>rd(person, {name = "", phone = [], address}).</input>
89
<p>This is due to the fact that record definitions are available
90
at compile time only, not at runtime. See <c>shell(3)</c> for
91
details on records in the shell.
96
<title>Creating a Record</title>
97
<p>A new <c>person</c> record is created as follows:</p>
99
> <input>#person{phone=[0,8,2,3,4,3,1,2], name="Robert"}.</input>
100
#person{name = "Robert",phone = [0,8,2,3,4,3,1,2],address = undefined}</pre>
101
<p>Since the <c>address</c> field was omitted, its default value
103
<p>There is a new feature introduced in Erlang 5.1/OTP R8B,
104
with which you can set a value to all fields in a record,
105
overriding the defaults in the record specification. The special
106
field <c>_</c>, means "all fields not explicitly specified".</p>
108
> <input>#person{name = "Jakob", _ = '_'}.</input>
109
#person{name = "Jakob",phone = '_',address = '_'}</pre>
110
<p>It is primarily intended to be used in <c>ets:match/2</c> and
111
<c>mnesia:match_object/3</c>, to set record fields to the atom
112
<c>'_'</c>. (This is a wildcard in <c>ets:match/2</c>.)</p>
116
<title>Accessing a Record Field</title>
118
> <input>P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.</input>
119
#person{name = "Joe",phone = [0,8,2,3,4,3,1,2],address = undefined}
120
> <input>P#person.name.</input>
125
<title>Updating a Record</title>
127
> <input>P1 = #person{name="Joe", phone=[1,2,3], address="A street"}.</input>
128
#person{name = "Joe",phone = [1,2,3],address = "A street"}
129
> <input>P2 = P1#person{name="Robert"}.</input>
130
#person{name = "Robert",phone = [1,2,3],address = "A street"}</pre>
134
<title>Type Testing</title>
135
<p>The following example shows that the guard succeeds if
136
<c>P</c> is record of type <c>person</c>.</p>
138
foo(P) when is_record(P, person) -> a_person;
139
foo(_) -> not_a_person.</pre>
143
<title>Pattern Matching</title>
144
<p>Matching can be used in combination with records as shown in
145
the following example:</p>
147
> <input>P3 = #person{name="Joe", phone=[0,0,7], address="A street"}.</input>
148
#person{name = "Joe",phone = [0,0,7],address = "A street"}
149
> <input>#person{name = Name} = P3, Name.</input>
151
<p>The following function takes a list of <c>person</c> records
152
and searches for the phone number of a person with a particular
155
find_phone([#person{name=Name, phone=Phone} | _], Name) ->
157
find_phone([_| T], Name) ->
159
find_phone([], Name) ->
161
<p>The fields referred to in the pattern can be given in any order.</p>
165
<title>Nested Records</title>
166
<p>The value of a field in a record might be an instance of a
167
record. Retrieval of nested data can be done stepwise, or in a
168
single step, as shown in the following example:</p>
170
-record(name, {first = "Robert", last = "Ericsson"}).
171
-record(person, {name = #name{}, phone}).
174
P = #person{name= #name{first="Robert",last="Virding"}, phone=123},
175
First = (P#person.name)#name.first.</pre>
176
<p>In this example, <c>demo()</c> evaluates to <c>"Robert"</c>.</p>
180
<title>Example</title>
184
%%-----------------------------------------------------------
187
%% name: A string (default is undefined).
188
%% age: An integer (default is undefined).
189
%% phone: A list of integers (default is []).
190
%% dict: A dictionary containing various information
192
%% A {Key, Value} list (default is the empty list).
193
%%------------------------------------------------------------
194
-record(person, {name, age, phone = [], dict = []}).</pre>
197
-include("person.hrl").
198
-compile(export_all). % For test purposes only.
200
%% This creates an instance of a person.
201
%% Note: The phone number is not supplied so the
202
%% default value [] will be used.
204
make_hacker_without_phone(Name, Age) ->
205
#person{name = Name, age = Age,
206
dict = [{computer_knowledge, excellent},
209
%% This demonstrates matching in arguments
211
print(#person{name = Name, age = Age,
212
phone = Phone, dict = Dict}) ->
213
io:format("Name: ~s, Age: ~w, Phone: ~w ~n"
214
"Dictionary: ~w.~n", [Name, Age, Phone, Dict]).
216
%% Demonstrates type testing, selector, updating.
218
birthday(P) when record(P, person) ->
219
P#person{age = P#person.age + 1}.
221
register_two_hackers() ->
222
Hacker1 = make_hacker_without_phone("Joe", 29),
223
OldHacker = birthday(Hacker1),
224
% The central_register_server should have
225
% an interface function for this.
226
central_register_server ! {register_person, Hacker1},
227
central_register_server ! {register_person,
228
OldHacker#person{name = "Robert",
229
phone = [0,8,3,2,4,5,3,1]}}.</pre>