aboutsummaryrefslogtreecommitdiffstats
path: root/system/doc/programming_examples/records.xml
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc/programming_examples/records.xml')
-rw-r--r--system/doc/programming_examples/records.xml232
1 files changed, 232 insertions, 0 deletions
diff --git a/system/doc/programming_examples/records.xml b/system/doc/programming_examples/records.xml
new file mode 100644
index 0000000000..2f2434f11c
--- /dev/null
+++ b/system/doc/programming_examples/records.xml
@@ -0,0 +1,232 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2003</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Records</title>
+ <prepared></prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>records.xml</file>
+ </header>
+
+ <section>
+ <title>Records vs Tuples</title>
+ <p>The main advantage of using records instead of tuples is that
+ fields in a record are accessed by name, whereas fields in a
+ tuple are accessed by position. To illustrate these differences,
+ suppose that we want to represent a person with the tuple
+ <c>{Name, Address, Phone}</c>.</p>
+ <p>We must remember that the <c>Name</c> field is the first
+ element of the tuple, the <c>Address</c> field is the second
+ element, and so on, in order to write functions which manipulate
+ this data. For example, to extract data from a variable <c>P</c>
+ which contains such a tuple we might write the following code
+ and then use pattern matching to extract the relevant fields.</p>
+ <code type="none">
+Name = element(1, P),
+Address = element(2, P),
+...</code>
+ <p>Code like this is difficult to read and understand and errors
+ occur if we get the numbering of the elements in the tuple wrong.
+ If we change the data representation by re-ordering the fields,
+ or by adding or removing a field, then all references to
+ the person tuple, wherever they occur, must be checked and
+ possibly modified.</p>
+ <p>Records allow us to refer to the fields by name and not
+ position. We use a record instead of a tuple to store the data.
+ If we write a record definition of the type shown below, we can
+ then refer to the fields of the record by name.</p>
+ <code type="none">
+-record(person, {name, phone, address}).</code>
+ <p>For example, if <c>P</c> is now a variable whose value is a
+ <c>person</c> record, we can code as follows in order to access
+ the name and address fields of the records.</p>
+ <code type="none">
+Name = P#person.name,
+Address = P#person.address,
+...</code>
+ <p>Internally, records are represented using tagged tuples:</p>
+ <code type="none">
+{person, Name, Phone, Address}</code>
+ </section>
+
+ <section>
+ <title>Defining a Record</title>
+ <p>This definition of a person will be used in many of
+ the examples which follow. It contains three fields, <c>name</c>,
+ <c>phone</c> and <c>address</c>. The default values for
+ <c>name</c> and <c>phone</c> is "" and [], respectively.
+ The default value for <c>address</c> is the atom
+ <c>undefined</c>, since no default value is supplied for this
+ field:</p>
+ <pre>
+-record(person, {name = "", phone = [], address}).</pre>
+ <p>We have to define the record in the shell in order to be able
+ use the record syntax in the examples:</p>
+ <pre>
+> <input>rd(person, {name = "", phone = [], address}).</input>
+person</pre>
+ <p>This is due to the fact that record definitions are available
+ at compile time only, not at runtime. See <c>shell(3)</c> for
+ details on records in the shell.
+ </p>
+ </section>
+
+ <section>
+ <title>Creating a Record</title>
+ <p>A new <c>person</c> record is created as follows:</p>
+ <pre>
+> <input>#person{phone=[0,8,2,3,4,3,1,2], name="Robert"}.</input>
+#person{name = "Robert",phone = [0,8,2,3,4,3,1,2],address = undefined}</pre>
+ <p>Since the <c>address</c> field was omitted, its default value
+ is used.</p>
+ <p>There is a new feature introduced in Erlang 5.1/OTP R8B,
+ with which you can set a value to all fields in a record,
+ overriding the defaults in the record specification. The special
+ field <c>_</c>, means "all fields not explicitly specified".</p>
+ <pre>
+> <input>#person{name = "Jakob", _ = '_'}.</input>
+#person{name = "Jakob",phone = '_',address = '_'}</pre>
+ <p>It is primarily intended to be used in <c>ets:match/2</c> and
+ <c>mnesia:match_object/3</c>, to set record fields to the atom
+ <c>'_'</c>. (This is a wildcard in <c>ets:match/2</c>.)</p>
+ </section>
+
+ <section>
+ <title>Accessing a Record Field</title>
+ <pre>
+> <input>P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.</input>
+#person{name = "Joe",phone = [0,8,2,3,4,3,1,2],address = undefined}
+> <input>P#person.name.</input>
+"Joe"</pre>
+ </section>
+
+ <section>
+ <title>Updating a Record</title>
+ <pre>
+> <input>P1 = #person{name="Joe", phone=[1,2,3], address="A street"}.</input>
+#person{name = "Joe",phone = [1,2,3],address = "A street"}
+> <input>P2 = P1#person{name="Robert"}.</input>
+#person{name = "Robert",phone = [1,2,3],address = "A street"}</pre>
+ </section>
+
+ <section>
+ <title>Type Testing</title>
+ <p>The following example shows that the guard succeeds if
+ <c>P</c> is record of type <c>person</c>.</p>
+ <pre>
+foo(P) when is_record(P, person) -> a_person;
+foo(_) -> not_a_person.</pre>
+ </section>
+
+ <section>
+ <title>Pattern Matching</title>
+ <p>Matching can be used in combination with records as shown in
+ the following example:</p>
+ <pre>
+> <input>P3 = #person{name="Joe", phone=[0,0,7], address="A street"}.</input>
+#person{name = "Joe",phone = [0,0,7],address = "A street"}
+> <input>#person{name = Name} = P3, Name.</input>
+"Joe"</pre>
+ <p>The following function takes a list of <c>person</c> records
+ and searches for the phone number of a person with a particular
+ name:</p>
+ <code type="none">
+find_phone([#person{name=Name, phone=Phone} | _], Name) ->
+ {found, Phone};
+find_phone([_| T], Name) ->
+ find_phone(T, Name);
+find_phone([], Name) ->
+ not_found.</code>
+ <p>The fields referred to in the pattern can be given in any order.</p>
+ </section>
+
+ <section>
+ <title>Nested Records</title>
+ <p>The value of a field in a record might be an instance of a
+ record. Retrieval of nested data can be done stepwise, or in a
+ single step, as shown in the following example:</p>
+ <pre>
+-record(name, {first = "Robert", last = "Ericsson"}).
+-record(person, {name = #name{}, phone}).
+
+demo() ->
+ P = #person{name= #name{first="Robert",last="Virding"}, phone=123},
+ First = (P#person.name)#name.first.</pre>
+ <p>In this example, <c>demo()</c> evaluates to <c>"Robert"</c>.</p>
+ </section>
+
+ <section>
+ <title>Example</title>
+ <pre>
+%% File: person.hrl
+
+%%-----------------------------------------------------------
+%% Data Type: person
+%% where:
+%% name: A string (default is undefined).
+%% age: An integer (default is undefined).
+%% phone: A list of integers (default is []).
+%% dict: A dictionary containing various information
+%% about the person.
+%% A {Key, Value} list (default is the empty list).
+%%------------------------------------------------------------
+-record(person, {name, age, phone = [], dict = []}).</pre>
+ <pre>
+-module(person).
+-include("person.hrl").
+-compile(export_all). % For test purposes only.
+
+%% This creates an instance of a person.
+%% Note: The phone number is not supplied so the
+%% default value [] will be used.
+
+make_hacker_without_phone(Name, Age) ->
+ #person{name = Name, age = Age,
+ dict = [{computer_knowledge, excellent},
+ {drinks, coke}]}.
+
+%% This demonstrates matching in arguments
+
+print(#person{name = Name, age = Age,
+ phone = Phone, dict = Dict}) ->
+ io:format("Name: ~s, Age: ~w, Phone: ~w ~n"
+ "Dictionary: ~w.~n", [Name, Age, Phone, Dict]).
+
+%% Demonstrates type testing, selector, updating.
+
+birthday(P) when record(P, person) ->
+ P#person{age = P#person.age + 1}.
+
+register_two_hackers() ->
+ Hacker1 = make_hacker_without_phone("Joe", 29),
+ OldHacker = birthday(Hacker1),
+ % The central_register_server should have
+ % an interface function for this.
+ central_register_server ! {register_person, Hacker1},
+ central_register_server ! {register_person,
+ OldHacker#person{name = "Robert",
+ phone = [0,8,3,2,4,5,3,1]}}.</pre>
+ </section>
+</chapter>
+