diff options
Diffstat (limited to 'system/doc/extensions')
-rw-r--r-- | system/doc/extensions/Makefile | 142 | ||||
-rw-r--r-- | system/doc/extensions/bit_syntax.xml | 403 | ||||
-rw-r--r-- | system/doc/extensions/book.xml | 45 | ||||
-rw-r--r-- | system/doc/extensions/fun_test.erl | 17 | ||||
-rw-r--r-- | system/doc/extensions/funparse.erl | 74 | ||||
-rw-r--r-- | system/doc/extensions/funs.xml | 486 | ||||
-rw-r--r-- | system/doc/extensions/funs1.erl | 125 | ||||
-rw-r--r-- | system/doc/extensions/include.xml | 81 | ||||
-rw-r--r-- | system/doc/extensions/list_comprehensions.erl | 118 | ||||
-rw-r--r-- | system/doc/extensions/list_comprehensions.xml | 205 | ||||
-rw-r--r-- | system/doc/extensions/list_comrehensions.erl | 75 | ||||
-rw-r--r-- | system/doc/extensions/macros.xml | 177 | ||||
-rw-r--r-- | system/doc/extensions/make.dep | 21 | ||||
-rw-r--r-- | system/doc/extensions/mexpand.erl | 16 | ||||
-rw-r--r-- | system/doc/extensions/misc.xml | 310 | ||||
-rw-r--r-- | system/doc/extensions/part.xml | 57 | ||||
-rw-r--r-- | system/doc/extensions/records.xml | 284 | ||||
-rw-r--r-- | system/doc/extensions/warning.gif | bin | 0 -> 1498 bytes |
18 files changed, 2636 insertions, 0 deletions
diff --git a/system/doc/extensions/Makefile b/system/doc/extensions/Makefile new file mode 100644 index 0000000000..cfc506f7e8 --- /dev/null +++ b/system/doc/extensions/Makefile @@ -0,0 +1,142 @@ +# ``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 via the world wide web 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. +# +# The Initial Developer of the Original Code is Ericsson Utvecklings AB. +# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +# AB. All Rights Reserved.'' +# +# $Id$ +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include $(ERL_TOP)/erts/vsn.mk +#VSN=$(SYSTEM_VSN) + +# ---------------------------------------------------- +# Include dependency +# ---------------------------------------------------- + +include make.dep + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/doc/extensions + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +XML_PART_FILES = part.xml +XML_CHAPTER_FILES = \ + funs.xml \ + macros.xml \ + misc.xml \ + include.xml \ + records.xml \ + list_comprehensions.xml \ + bit_syntax.xml + +BOOK_FILES = book.xml + +GIF_FILES = note.gif + +PS_FILES = + +# ---------------------------------------------------- + +HTML_FILES = \ + $(XML_PART_FILES:%.xml=%.html) + +HTMLDIR = . +EXTRA_FILES = $(DEFAULT_GIF_FILES) \ + $(DEFAULT_HTML_FILES) \ + $(XML_CHAPTER_FILES:%.xml=%.html) + +TEX_FILES_BOOK = \ + $(BOOK_FILES:%.xml=%.tex) +TEX_FILES_USERS_GUIDE = \ + $(XML_CHAPTER_FILES:%.xml=%.tex) + +TOP_PDF_FILE = extensions-$(VSN).pdf +TOP_PS_FILE = extensions-$(VSN).ps + +$(TOP_PDF_FILE): book.dvi $(ERL_TOP)/erts/vsn.mk + $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@ + +$(TOP_PS_FILE): book.dvi $(ERL_TOP)/erts/vsn.mk + $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@ + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +XML_FLAGS += +DVIPS_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +ifeq ($(DOCTYPE),pdf) +docs: pdf +else +ifeq ($(DOCTYPE),ps) +docs: ps +else +docs: html +endif +endif + +pdf: $(TOP_PDF_FILE) + +ps: $(TOP_PS_FILE) + +html: $(HTML_FILES) $(GIF_FILES) + +debug opt: + +clean_tex: + -rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_BOOK) + +clean: + rm -f *.html $(TEX_FILES_USERS_GUIDE) + rm -f $(TOP_PS_FILES) + rm -f errs core *~ $(LATEX_CLEAN) + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +ifeq ($(DOCTYPE),pdf) +release_docs_spec: pdf + $(INSTALL_DIR) $(RELEASE_PATH)/pdf + $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +else +ifeq ($(DOCTYPE),ps) +release_docs_spec: ps + $(INSTALL_DIR) $(RELEASE_PATH)/ps + $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps +else +release_docs_spec: docs + $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \ + $(RELSYSDIR) +endif +endif + +release_spec: + + + diff --git a/system/doc/extensions/bit_syntax.xml b/system/doc/extensions/bit_syntax.xml new file mode 100644 index 0000000000..d86f73cd9a --- /dev/null +++ b/system/doc/extensions/bit_syntax.xml @@ -0,0 +1,403 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2000</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>The Bit Syntax</title> + <prepared>Björn Gustavsson</prepared> + <responsible>Bjarne Däcker</responsible> + <docno>1</docno> + <approved>Bjarne DäKer</approved> + <checked></checked> + <date>00-06-21</date> + <rev>PA1</rev> + <file>bit_syntax.sgml</file> + </header> + <p>This section describes the "bit syntax" which was added to + the Erlang language in release 5.0 (R7). + Compared to the original bit syntax prototype + by Claes Wikström and Tony Rogvall (presented on the + Erlang User's Conference 1999), this implementation differs + primarily in the following respects, + </p> + <list type="ordered"> + <item> + <p>the character pairs '<<' and '>>' are used to delimit + a binary patterns and constructor (not '<' and '>' as in + the prototype), + </p> + </item> + <item> + <p>the tail syntax ('|Variable') has been eliminated, + </p> + </item> + <item> + <p>all size expressions must be bound, + </p> + </item> + <item> + <p>a type <c>unit:U</c> has been added, + </p> + </item> + <item> + <p>lists and tuples cannot be generated + </p> + </item> + <item> + <p>there are no paddings whatsoever. + </p> + </item> + </list> + + <section> + <title>Introduction</title> + <p>In Erlang a Bin is used for constructing binaries and + matching binary patterns. A Bin is written with the + following syntax:</p> + <code type="none"><![CDATA[ + <<E1, E2, ... En>> + ]]></code> + <p>A Bin is a low-level sequence of bytes. The purpose of a + Bin is to be able to, from a high level, + <em>construct</em> a binary, + </p> + <code type="none"><![CDATA[ + Bin = <<E1, E2, ... En>> + ]]></code> + <p>in which case all elements must be bound, or to + <em>match</em> a binary, + </p> + <code type="none"><![CDATA[ + <<E1, E2, ... En>> = Bin + ]]></code> + <p>where <c>Bin</c> is bound, and where the elements are bound or unbound, + as in any match. + </p> + <p>Each element specifies a certain <em>segment</em> of the binary. + A segment is is a set of contiguous bits of the binary (not + necessarily on a byte boundary). The first element specifies + the initial segment, the second element specifies the following + segment etc. + </p> + <p>The following examples illustrate how binaries are constructed + or matched, and how elements and tails are specified. + </p> + + <section> + <title>Examples</title> + <p><em>Example 1: </em>A binary can be constructed from a set of + constants or a string literal:</p> + <code type="none"><![CDATA[ + Bin11 = <<1, 17, 42>>, + Bin12 = <<"abc">> + ]]></code> + <p>yields binaries of size 3; <c>binary_to_list(Bin11)</c> + evaluates to <c>[1, 17, 42]</c>, and + <c>binary_to_list(Bin12)</c> evaluates to <c>[97, 98, 99]</c>. + </p> + <p><em>Example 2: </em>Similarly, a binary can be constructed + from a set of bound variables:</p> + <code type="none"><![CDATA[ + A = 1, B = 17, C = 42, + Bin2 = <<A, B, C:16>> + ]]></code> + <p>yields a binary of size 4, and <c>binary_to_list(Bin2)</c> + evaluates to <c>[1, 17, 00, 42]</c> too. Here we used a + <em>size expression</em> for the variable <c>C</c> in order to + specify a 16-bits segment of <c>Bin2</c>. + </p> + <p><em>Example 3: </em>A Bin can also be used for matching: if + <c>D</c>, <c>E</c>, and <c>F</c> are unbound variables, and + <c>Bin2</c> is bound as in the former example,</p> + <code type="none"><![CDATA[ + <<D:16, E, F/binary>> = Bin2 + ]]></code> + <p>yields <c>D = 273</c>, <c>E = 00</c>, and F binds to a binary + of size 1: <c>binary_to_list(F) = [42]</c>. + </p> + <p><em>Example 4: </em>The following is a more elaborate example + of matching, where <c>Dgram</c> is bound to the consecutive + bytes of an IP datagram of IP protocol version 4, and where we + want to extract the header and the data of the datagram:</p> + <code type="none"><![CDATA[ + -define(IP_VERSION, 4). + -define(IP_MIN_HDR_LEN, 5). + + DgramSize = byte_size(Dgram), + case Dgram of + <<?IP_VERSION:4, HLen:4, SrvcType:8, TotLen:16, + ID:16, Flgs:3, FragOff:13, + TTL:8, Proto:8, HdrChkSum:16, + SrcIP:32, + DestIP:32, RestDgram/binary>> when HLen >= 5, 4*HLen =< DgramSize -> + OptsLen = 4*(HLen - ?IP_MIN_HDR_LEN), + <<Opts:OptsLen/binary,Data/binary>> = RestDgram, + ... + end. + ]]></code> + <p>Here the segment corresponding to the <c>Opts</c> variable + has a <em>type modifier</em> specifying that <c>Opts</c> should + bind to a binary. All other variables have the default type + equal to unsigned integer. + </p> + <p>An IP datagram header is of variable length, and its length - + measured in the number of 32-bit words - is given in the segment + corresponding to <c>HLen</c>, the minimum value of which is + 5. It is the segment corresponding to <c>Opts</c> that is + variable: if <c>HLen</c> is equal to 5, <c>Opts</c> will be an + empty binary. + </p> + <p>The tail variables <c>RestDgram</c> and <c>Data</c> bind to + binaries, as all tail variables do. Both may bind to empty + binaries. + </p> + <p>If the first 4-bits segment of <c>Dgram</c> is not equal to + 4, or if <c>HLen</c> is less than 5, or if the size of + <c>Dgram</c> is less than <c>4*HLen</c>, the match of + <c>Dgram</c> fails. + </p> + </section> + </section> + + <section> + <title>A Lexical Note</title> + <p>Note that "<c><![CDATA[B=<<1>>]]></c>" will be interpreted as + "<c><![CDATA[B =< ;<1>>]]></c>", which is a syntax error. + The correct way to write the expression is "<c><![CDATA[B = <<1>>]]></c>".</p> + </section> + + <section> + <title>Segments</title> + <p>Each segment has the following general syntax:</p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>Both the <c>Size</c> and the <c>TypeSpecifier</c> or both may be + omitted; thus the following variations are allowed: + </p> + <p><c>Value</c></p> + <p><c>Value:Size</c></p> + <p><c>Value/TypeSpecifierList</c></p> + <p>Default values will be used for missing specifications. The default + values are described in the section "Defaults" below. + </p> + <p>Used in binary construction, the <c>Value</c> part is any expression. + Used in binary matching, the <c>Value</c> part must be a literal or + variable. You can read more about the <c>Value</c> part in the + sections about constructing binaries and matching binaries. + </p> + <p>The <c>Size</c> part of the segment multiplied by the unit in the + <c>TypeSpecifierList</c> (described below) gives the number of bits + for the segment. In construction, <c>Size</c> is any expression that + evaluates to an integer. In matching, <c>Size</c> must be a constant + expression or a variable. + </p> + <p>The <c>TypeSpecifierList</c> is a list of type specifiers separated by + hyphens. + </p> + <taglist> + <tag>Type</tag> + <item>The type can be <c>integer</c>, <c>float</c>, or <c>binary</c>.</item> + <tag>Signedness</tag> + <item>The signedness specification can be either <c>signed</c> + or <c>unsigned</c>. Note that signedness only matters for matching.</item> + <tag>Endianness</tag> + <item>The endianness specification can be either <c>big</c>, + <c>little</c>, or <c>native</c>. Native-endian means that + the endian will be resolved at load time to be either + big-endian or little-endian, depending on what is "native" + for the CPU that the Erlang machine is run on.</item> + <tag>Unit</tag> + <item>The unit size is given as <c>unit:IntegerLiteral</c>. + The allowed range is 1-256. It will be multiplied by the <c>Size</c> + specifier to give the effective size of the segment.</item> + </taglist> + <p>Example: + </p> + <code type="none"> +X:4/little-signed-integer-unit:8 + </code> + <p>This element has a total size of 4*8 = 32 bits, and it contains a + signed integer in little-endian order.</p> + </section> + + <section> + <title>Defaults</title> + <p>The default type for a segment is <c>integer</c>. The default type + does <em>not</em> depend on the value, even if the value is a literal. + For instance, the default type in '<c><![CDATA[<<3.14>>]]></c>' is <c>integer</c>, + not <c>float</c>. + </p> + <p>The default <c>Size</c> depends on the type. + For <c>integer</c> it is 8. For <c>float</c> it is 64. + For <c>binary</c> it is all of the binary. In matching, this default + value is only valid for the very last element. All other binary elements + in matching must have a size specification. + </p> + <p>The default unit depends on the the type. + For <c>integer</c> and <c>float</c> it is 1. + For <c>binary</c> it is 8. + </p> + <p>The default signedness is <c>unsigned</c>. + </p> + <p>The default endianness is <c>big</c>.</p> + </section> + + <section> + <title>Constructing Binaries</title> + <p>This section describes the rules for constructing binaries using + the bit syntax. Unlike when constructing lists or tuples, the construction + of a binary can fail with a <c>badarg</c> exception. + </p> + <p>There can be zero or more segments in a binary to be constructed. + The expression '<c><![CDATA[<<>>]]></c>' constructs a zero length binary. + </p> + <p>Each segment in a binary can consist of zero or more bits. + There are no alignment rules for individual segments, but the total + number of bits in all segments must be evenly divisible by 8, + or in other words, the resulting binary must consist of a whole number + of bytes. An <c>badarg</c> exception will be thrown if the resulting + binary is not byte-aligned. Example: + </p> + <code type="none"><![CDATA[ +<<X:1,Y:6>> + ]]></code> + <p>The total number of bits is 7, which is not evenly divisible by 8; + thus, there will be <c>badarg</c> exception (and a compiler warning + as well). The following example + </p> + <code type="none"><![CDATA[ +<<X:1,Y:6,Z:1>> + ]]></code> + <p>will successfully construct a binary of 8 bits, or one byte. (Provided + that all of X, Y and Z are integers.) + </p> + <p>As noted earlier, segments have the following general syntax: + </p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>When constructing binaries, <c>Value</c> and <c>Size</c> can be + any Erlang expression. However, for syntactical reasons, + both <c>Value</c> and <c>Size</c> must be enclosed in parenthesis + if the expression consists of anything more than a single literal + or variable. The following gives a compiler syntax error: + </p> + <code type="none"><![CDATA[ +<<X+1:8>> + ]]></code> + <p>This expression must be rewritten to + </p> + <code type="none"><![CDATA[ +<<(X+1):8>> + ]]></code> + <p>in order to be accepted by the compiler. + </p> + + <section> + <title>Including Literal Strings</title> + <p>As syntactic sugar, an literal string may be written instead of a + element.</p> + <code type="none"><![CDATA[ +<<"hello">> ]]></code> + <p>which is syntactic sugar for</p> + <code type="none"><![CDATA[ +<<$h,$e,$l,$l,$o>> ]]></code> + </section> + </section> + + <section> + <title>Matching Binaries</title> + <p>This section describes the rules for matching binaries using the + bit syntax. + </p> + <p>There can be zero or more segments in a binary binary pattern. + A binary pattern can occur in every place patterns are allowed, also + inside other patterns. Binary patterns cannot be nested. + </p> + <p>The pattern '<c><![CDATA[<<>>]]></c>' matches a zero length binary. + </p> + <p>Each segment in a binary can consist of zero or more bits. + </p> + <p>A segment of type <c>binary</c> must have a size evenly divisible by 8. + </p> + <p>This means that the following head will never match:</p> + <code type="none"><![CDATA[ +foo(<<X:7/binary,Y:1/binary>>) -> ]]></code> + <p>As noted earlier, segments have the following general syntax: + </p> + <p><c>Value:Size/TypeSpecifierList</c></p> + <p>When matching <c>Value</c> value must be either a variable or an integer + or floating point literal. Expressions are not allowed. + </p> + <p><c>Size</c> must be an integer literal, or a previously bound variable. + Note that the following is not allowed:</p> + <code type="none"><![CDATA[ +foo(N, <<X:N,T/binary>>) -> + {X,T}. ]]></code> + <p>The two occurrences of <c>N</c> are not related. The compiler + will complain that the <c>N</c> in the size field is unbound. + </p> + <p>The correct way to write this example is like this:</p> + <code type="none"><![CDATA[ +foo(N, Bin) -> + <<X:N,T/binary>> = Bin, + {X,T}. ]]></code> + + <section> + <title>Getting the Rest of the Binary</title> + <p>To match out the rest of binary, specify a binary field without size:</p> + <code type="none"><![CDATA[ +foo(<<A:8,Rest/binary>>) -> ]]></code> + <p>As always, the size of the tail must be evenly divisible by 8. + </p> + </section> + </section> + + <section> + <title>Traps and Pitfalls</title> + <p>Assume that we need a function that creates a binary out of a + list of triples of integers. A first (inefficient) version of such + a function could look like this:</p> + <code type="none"><![CDATA[ +triples_to_bin(T) -> + triples_to_bin(T, <<>>). + +triples_to_bin([{X,Y,Z} | T], Acc) -> + triples_to_bin(T, <<Acc/binary, X:32, Y:32, Z:32>>); % inefficient +triples_to_bin([], Acc) -> + Acc. ]]></code> + <p>The reason for the inefficiency of this function is that for + each triple, the binary constructed so far (<c>Acc</c>) is copied. + (Note: The original bit syntax prototype avoided the copy operation + by using segmented binaries, which are not implemented in R7.) + </p> + <p>The efficient way to write this function in R7 is:</p> + <code type="none"><![CDATA[ +triples_to_bin(T) -> + triples_to_bin(T, []). + +triples_to_bin([{X,Y,Z} | T], Acc) -> + triples_to_bin(T, [<<X:32, Y:32, Z:32>> | Acc]); +triples_to_bin([], Acc) -> + list_to_binary(lists:reverse(Acc)). ]]></code> + <p>Note that <c>list_to_binary/1</c> handles deep lists of binaries + and small integers. (This fact was previously undocumented.) + </p> + </section> +</chapter> + diff --git a/system/doc/extensions/book.xml b/system/doc/extensions/book.xml new file mode 100644 index 0000000000..ffdbe6cb44 --- /dev/null +++ b/system/doc/extensions/book.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE book SYSTEM "book.dtd"> + +<book> + <header titlestyle="normal"> + <copyright> + <year>1997</year> + <year>2007</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Erlang Extensions Since 4.4</title> + <prepared>OTP Team</prepared> + <docno></docno> + <date>1997-05-21</date> + <rev></rev> + <file>book.sgml</file> + </header> + <insidecover> + </insidecover> + <pagetext>Erlang Extensions Since 4.4</pagetext> + <preamble> + <contents level="2"></contents> + </preamble> + <parts lift="no"> + <include file="part"></include> + </parts> + <listofterms></listofterms> + <index></index> +</book> + diff --git a/system/doc/extensions/fun_test.erl b/system/doc/extensions/fun_test.erl new file mode 100644 index 0000000000..8472fd87f8 --- /dev/null +++ b/system/doc/extensions/fun_test.erl @@ -0,0 +1,17 @@ +%1 +-module(fun_test). +-export([t1/0, t2/0, t3/0, t4/0, double/1]). +-import(lists, [map/2]). + +t1() -> map(fun(X) -> 2 * X end, [1,2,3,4,5]). + +t2() -> map(fun double/1, [1,2,3,4,5]). + +t3() -> map({?MODULE, double}, [1,2,3,4,5]). + +double(X) -> X * 2. +%1 + + +t4() -> + "hello world". diff --git a/system/doc/extensions/funparse.erl b/system/doc/extensions/funparse.erl new file mode 100644 index 0000000000..5e23c90df9 --- /dev/null +++ b/system/doc/extensions/funparse.erl @@ -0,0 +1,74 @@ +-module(funparse). +-compile(export_all). +-import(lists, [reverse/1]). + +%17 +%% > hof:parse([a,c]). +%% {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} +%% > hof:parse([a,d]). +%% {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} +%% > hof:parse([b,c]). +%% {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} +%% > hof:parse([b,d]). +%% {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} +%% > hof:parse([a,b]). +%% fail +%17 + +%% Grammar = (a | b) & (c | d) + +%12 +parse(List) -> + (grammar())(List). +%12 + +%13 +grammar() -> + pand( + por(pconst(a), pconst(b)), + por(pconst(c), pconst(d))). +%13 + +%14 +pconst(X) -> + fun (T) -> + case T of + [X|T1] -> {ok, {const, X}, T1}; + _ -> fail + end + end. +%14 + +%15 +por(P1, P2) -> + fun (T) -> + case P1(T) of + {ok, R, T1} -> + {ok, {'or',1,R}, T1}; + fail -> + case P2(T) of + {ok, R1, T1} -> + {ok, {'or',2,R1}, T1}; + fail -> + fail + end + end + end. +%15 + +%16 +pand(P1, P2) -> + fun (T) -> + case P1(T) of + {ok, R1, T1} -> + case P2(T1) of + {ok, R2, T2} -> + {ok, {'and', R1, R2}}; + fail -> + fail + end; + fail -> + fail + end + end. +%16 diff --git a/system/doc/extensions/funs.xml b/system/doc/extensions/funs.xml new file mode 100644 index 0000000000..f9c003c8ee --- /dev/null +++ b/system/doc/extensions/funs.xml @@ -0,0 +1,486 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year> + <year>2007</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Programming with Funs</title> + <prepared>Joe Armstrong</prepared> + <responsible>Bjarne Däcker</responsible> + <docno>1</docno> + <approved>Bjarne DäKer</approved> + <checked></checked> + <date>96-01-16</date> + <rev>PA1</rev> + <file>funs.sgml</file> + </header> + <p>This section introduces functional objects (Funs). That is a new data type introduced in Erlang 4.4. Functions which takes Funs as arguments, or which return Funs are called higher order functions.</p> + <list type="bulleted"> + <item>Funs can be passed as arguments to other functions, just like lists or tuples</item> + <item>functions can be written which return Funs, just like any other data object.</item> + </list> + + <section> + <title>Higher Order Functions </title> + <p>Funs encourages us to encapsulate common patterns of design into functional forms called higher order functions. These functions not only shortens programs, but also produce clearer programs because the intended meaning of the program is explicitly rather than implicitly stated.</p> + <p>The concepts of higher order functions and procedural abstraction are introduced with two brief examples.</p> + + <section> + <title>Example 1 - map</title> + <p>If we want to double every element in a list, we could write a function named <c>double</c>:</p> + <code type="none"> + +double([H|T]) -> [2*H|double(T)]; +double([]) -> [] </code> + <p>This function obviously doubles the argument entered as input as follows:</p> + <pre> + +> <input>double([1,2,3,4]).</input> +[2,4,6,8] </pre> + <p>We now add the function <c>add_one</c>, which adds one to every element in a list:</p> + <code type="none"> + +add_one([H|T]) -> [H+1|add_one(T)]; +add_one([]) -> []. </code> + <p>These functions, <c>double</c> and <c>add_one</c>, have a very similar structure. We can exploit this fact and write a function <c>map</c> which expresses this similarity:</p> + <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> + <p>We can now express the functions <c>double</c> and <c>add_one</c> in terms of <c>map</c> as follows:</p> + <code type="none"> + +double(L) -> map(fun(X) -> 2*X end, L). +add_one(L) -> map(fun(X) -> 1 + X end, L). </code> + <p><c>map(F, List)</c> is a function which takes a function <c>F</c> and a list <c>L</c> as arguments and + returns the new list which is obtained by applying <c>F</c> to each of the elements in <c>L</c>.</p> + <p>The process of abstracting out the common features of a number of different programs is called procedural abstraction. Procedural abstraction can be used in order to write several different functions which have a similar structure, but differ only in some minor detail. This is done as follows:</p> + <list type="ordered"> + <item>write one function which represents the common features of these functions</item> + <item>parameterize the difference in terms of functions which are passed as arguments to the common function.</item> + </list> + </section> + + <section> + <title>Example 2 - foreach</title> + <p>This example illustrates procedural abstraction. Initially, we show the following two examples written as conventional functions:</p> + <list type="ordered"> + <item>all elements of a list are printed onto a stream</item> + <item>a message is broadcast to a list of processes.</item> + </list> + <code type="none"> + +print_list(Stream, [H|T]) -> + io:format(Stream, "~p~n", [H]), + print_list(Stream, T); +print_on_list(Stream, []) -> + true. </code> + <code type="none"> + +broadcast(Msg, [Pid|Pids]) -> + Pid ! Msg, + broadcast(Msg, Pids); +broadcast(_, []) -> + true. </code> + <p>Both these functions have a very similar structure. They both iterate over a list doing something to each element in the list. The "something" has to be carried round as an extra argument to the function which does this.</p> + <p>The function <c>foreach</c> expresses this similarity:</p> + <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> + <p>Using <c>foreach</c>, <c>print_on_list</c> becomes:</p> + <code type="none"> + +foreach(fun(H) -> io:format(S, "~p~n~,[H]) end, L) </code> + <p><c>broadcast</c> becomes:</p> + <code type="none"> + +foreach(fun(Pid) -> Pid ! M end, L) </code> + <p><c>foreach</c> is evaluated for its side-effect and not its value. <c>foreach(Fun ,L)</c> calls <c>Fun(X)</c> for each element <c>X</c> in <c>L</c> and the processing occurs in the order in which the elements were defined in <c>L</c>. <c>map</c> does not define the order in which its elements are processed.</p> + </section> + </section> + + <section> + <title>Advantages of Higher Order Functions</title> + <p>Programming with higher order functions, such as <c>map</c> and <c>foreach</c>, has a number of advantages:</p> + <list type="bulleted"> + <item>It is much easier to understand the program and the intention of the programmer is clearly expressed in the code. The statement <c>foreach(fun(X) -></c> clearly indicates that the intention of this program is to do something to each element in the list <c>L</c>. We also know that the function which is passed as the first argument of <c>foreach</c> takes one argument <c>X</c>, which will be successively bound to each of the elements in <c>L</c>.</item> + <item>Functions which take Funs as arguments are much easier to re-use than other functions. </item> + </list> + </section> + + <section> + <title>The Syntax of Funs</title> + <p>Funs are written with the syntax:</p> + <code type="none"> + +F = fun (Arg1, Arg2, ... ArgN) -> + ... + end </code> + <p>This creates an anonymous function of <c>N</c> arguments and binds it to the variable <c>F</c>.</p> + <p>If we have already written a function in the same module and wish to pass this function as an argument, we can use the following syntax:</p> + <code type="none"> + +F = fun FunctionName/Arity </code> + <p>With this form of function reference, the function which is referred to does not need to be exported from the module.</p> + <p>We can also refer to a function defined in a different module with the following syntax:</p> + <code type="none"> + +F = {Module, FunctionName} </code> + <p>In this case, the function must be exported from the module in question.</p> + <p>The follow program illustrates the different ways of creating Funs:</p> + <codeinclude file="fun_test.erl" tag="%1" type="erl"></codeinclude> + <p>We can evaluate the fun <c>F</c> with the syntax:</p> + <code type="none"> + +F(Arg1, Arg2, ..., Argn) </code> + <p>To check whether a term is a Fun, use the test <c>function/1</c> + in a guard. Example:</p> + <code type="none"> +f(F, Args) when function(F) -> + apply(F, Args); +f(N, _) when integer(N) -> + N. </code> + <p>Funs are a distinct type. The BIFs erlang:fun_info/1,2 can + be used to retrieve information about a fun, and the BIF + erlang:fun_to_list/1 returns a textual representation of a fun. + The check_process_code/2 BIF returns true if the + process contains funs that depend on the old version of + a module.</p> + <note> + <p>In OTP R5 and earlier releases, funs were represented + using tuples.</p> + </note> + </section> + + <section> + <title>Variable Bindings within a Fun</title> + <p>The scope rules for variables which occur in Funs are as follows:</p> + <list type="bulleted"> + <item>All variables which occur in the head of a Fun are assumed to be "fresh" variables.</item> + <item>Variables which are defined before the Fun, and which occur in function calls or guard tests within the Fun, have the values they had outside the Fun.</item> + <item>No variables may be exported from a Fun.</item> + </list> + <p>The following examples illustrate these rules:</p> + <code type="none"> + +print_list(File, List) -> + {ok, Stream} = file:open(File, write), + foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List), + file:close(Stream). </code> + <p>In the above example, the variable <c>X</c> which is defined in the head of the Fun is a new variable. The value of the variable <c>Stream</c> which is used within within the Fun gets its value from the + <c>file:open</c> line.</p> + <p>Since any variable which occurs in the head of a Fun is considered a new variable it would be equally valid to write:</p> + <code type="none"> + +print_list(File, List) -> + {ok, Stream} = file:open(File, write), + foreach(fun(File) -> + io:format(Stream,"~p~n",[File]) + end, List), + file:close(Stream). </code> + <p>In this example, <c>File</c> is used as the new variable instead of <c>X</c>. This is rather silly since code in the body of the Fun cannot refer to the variable <c>File</c> which is defined outside the Fun. Compiling this example will yield the diagnostic:</p> + <code type="none"> + +./FileName.erl:Line: Warning: variable 'File' + shadowed in 'lambda head' </code> + <p>This reminds us that the variable <c>File</c> which is defined inside the Fun collides with the variable <c>File</c> which is defined outside the Fun.</p> + <p>The rules for importing variables into a Fun has the consequence that certain pattern matching operations have to be moved into guard expressions and cannot be written in the head of the Fun. For example, we might write the following code if we intend the first clause of <c>F</c> to be evaluated when the value of its argument is <c>Y</c>:</p> + <code type="none"> + +f(...) -> + Y = ... + map(fun(X) when X == Y -> + ; + (_) -> + ... + end, ...) + ... </code> + <p>instead of</p> + <code type="none"> + +f(...) -> + Y = ... + map(fun(Y) -> + ; + (_) -> + ... + end, ...) + ... </code> + </section> + + <section> + <title>Funs and the Module Lists</title> + <p>The following examples show a dialogue with the Erlang shell. All the higher order functions discussed are exported from the module <c>lists</c>.</p> + + <section> + <title>map</title> + <codeinclude file="funs1.erl" tag="%1" type="erl"></codeinclude> + <p><c>map</c> takes a function of one argument and a list of terms. It returns the list obtained by applying the function to every argument in the list.</p> + <pre> + +1> <input>Double = fun(X) -> 2 * X end.</input> +#Fun<erl_eval> +2><input>lists:map(Double, [1,2,3,4,5]).</input> +[2,4,6,8,10] </pre> + <p>When a new Fun is defined in the shell, the value of the Fun is printed as <c><![CDATA[Fun#<erl_eval>]]></c></p> + </section> + + <section> + <title>any</title> + <codeinclude file="funs1.erl" tag="%4" type="erl"></codeinclude> + <p><c>any</c> takes a predicate <c>P</c> of one argument and a list of terms. A predicate is a function which returns <c>true</c> or <c>false</c>. <c>any</c> is true if there is a term <c>X</c> in the list such that <c>P(X)</c> is <c>true</c>.</p> + <p>We define a predicate <c>Big(X)</c> which is <c>true</c> if its argument is greater that 10.</p> + <pre> + +3> <input>Big = fun(X) -> if X > 10 -> true; true -> false end end.</input> +#Fun<erl_eval> +4><input>lists:any(Big, [1,2,3,4]).</input> +false. +5> <input>lists:any(Big, [1,2,3,12,5]).</input> +true. </pre> + </section> + + <section> + <title>all</title> + <codeinclude file="funs1.erl" tag="%3" type="erl"></codeinclude> + <p><c>all</c> has the same arguments as <c>any</c>. It is true if the predicate applied to all elements in the list is true.</p> + <pre> + +6><input>lists:all(Big, [1,2,3,4,12,6]).</input> +false +7><input>lists:all(Big, [12,13,14,15]).</input> +true </pre> + </section> + + <section> + <title>foreach</title> + <codeinclude file="funs1.erl" tag="%2" type="erl"></codeinclude> + <p><c>foreach</c> takes a function of one argument and a list of terms. The function is applied to each argument in the list. <c>foreach</c> returns <c>ok</c>. It is used for its side-effect only.</p> + <pre> + +8> <input>lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]).</input> +1 +2 +3 +4 +true </pre> + </section> + + <section> + <title>foldl</title> + <codeinclude file="funs1.erl" tag="%8" type="erl"></codeinclude> + <p><c>foldl</c> takes a function of two arguments, an accumulator and a list. The function is called with two arguments. The first argument is the successive elements in the list, the second argument is the accumulator. The function must return a new accumulator which is used the next time the function is called.</p> + <p>If we have a list of lists <c>L = ["I","like","Erlang"]</c>, then we can sum the lengths of all the strings in <c>L</c> as follows:</p> + <pre> + +9> <input>L = ["I","like","Erlang"].</input> +["I","like","Erlang"] +10> <input>lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L).</input> +11 </pre> + <p><c>foldl</c> works like a <c>while</c> loop in an imperative language:</p> + <code type="none"> + + L = ["I","like","Erlang"], + Sum = 0, + while( L != []){ + Sum += length(head(L)), + L = tail(L) + end </code> + </section> + + <section> + <title>mapfoldl</title> + <codeinclude file="funs1.erl" tag="%10" type="erl"></codeinclude> + <p><c>mapfoldl</c> simultaneously maps and folds over a list. The following example shows how to change all letters in <c>L</c> to upper case and count them.</p> + <p>First upcase:</p> + <pre> + +11> <input>Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a;</input> +<input>(X) -> X</input> +<input>end.</input> +#Fun<erl_eval> +12> <input>Upcase_word =</input> +<input>fun(X) -></input> +<input>lists:map(Upcase, X)</input> +<input>end.</input> +#Fun<erl_eval> +13><input>Upcase_word("Erlang").</input> +"ERLANG" +14><input>lists:map(Upcase_word, L).</input> +["I","LIKE","ERLANG"] </pre> + <p>Now we can do the fold and the map at the same time:</p> + <pre> + +14> <input>lists:mapfoldl(fun(Word, Sum) -></input> +14> <input>{Upcase_word(Word), Sum + length(Word)}</input> +14> <input>end, 0, L).</input> +{["I","LIKE","ERLANG"],11} </pre> + </section> + + <section> + <title>filter</title> + <codeinclude file="funs1.erl" tag="%9" type="erl"></codeinclude> + <p><c>filter</c> takes a predicate of one argument and a list and returns all element in the list which satisfy the predicate.</p> + <pre> + +15><input>lists:filter(Big, [500,12,2,45,6,7]).</input> +[500,12,45] </pre> + <p>When we combine maps and filters we can write very succinct and obviously correct code. For example, suppose we want to define a set difference function. We want to define <c>diff(L1, L2)</c> to be the difference between the lists <c>L1</c> and <c>L2</c>. + This is the list of all elements in L1 which are not contained in L2. This code can be written as follows:</p> + <code type="none"> + +diff(L1, L2) -> + filter(fun(X) -> not member(X, L2) end, L1). </code> + <p>The AND intersection of the list <c>L1</c> and <c>L2</c> is also easily defined:</p> + <code type="none"> + +intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2). </code> + </section> + + <section> + <title>takewhile</title> + <codeinclude file="funs1.erl" tag="%5" type="erl"></codeinclude> + <p><c>takewhile(P, L)</c> takes elements <c>X</c> from a list <c>L</c> as long as the predicate <c>P(X)</c> is true.</p> + <pre> + +16><input>lists:takewhile(Big, [200,500,45,5,3,45,6]).</input> +[200,500,45] </pre> + </section> + + <section> + <title>dropwhile</title> + <codeinclude file="funs1.erl" tag="%6" type="erl"></codeinclude> + <p><c>dropwhile</c> is the complement of <c>takewhile</c>.</p> + <pre> + +17> <input>lists:dropwhile(Big, [200,500,45,5,3,45,6]).</input> +[5,3,45,6] </pre> + </section> + + <section> + <title>splitlist</title> + <codeinclude file="funs1.erl" tag="%7" type="erl"></codeinclude> + <p><c>splitlist(P, L)</c> splits the list <c>L</c> into the two sub-lists <c>{L1, L2}</c>, where <c>L = takewhile(P, L)</c> and <c>L2 = dropwhile(P, L)</c>.</p> + <pre> + +18><input>lists:splitlist(Big, [200,500,45,5,3,45,6]).</input> +{[200,500,45],[5,3,45,6]} </pre> + </section> + + <section> + <title>first</title> + <codeinclude file="funs1.erl" tag="%11" type="erl"></codeinclude> + <p><c>first</c> returns <c>{true, R}</c>, where <c>R</c> is the first element in a list satisfying a predicate or <c>false</c>:</p> + <pre> + +19><input>lists:first(Big, [1,2,45,6,123]).</input> +{true,45} +20><input>lists:first(Big, [1,2,4,5]). </input> +false </pre> + </section> + </section> + + <section> + <title>Funs which Return Funs</title> + <p>So far, this section has only described functions which take Funs as arguments. It is also possible to write more powerful functions which themselves return Funs. The following examples illustrate these type of functions.</p> + + <section> + <title>Simple Higher Order Functions</title> + <p><c>Adder(X)</c> is a function which, given <c>X</c>, returns a new function <c>G</c> such that <c>G(K)</c> returns <c>K + X</c>.</p> + <pre> + +21> <input>Adder = fun(X) -> fun(Y) -> X + Y end end.</input> +#Fun<erl_eval> +22> <input>Add6 = Adder(6).</input> +#Fun<erl_eval> +23><input>Add6(10).</input> +16 </pre> + </section> + + <section> + <title>Infinite Lists</title> + <p>The idea is to write something like:</p> + <code type="none"> + +-module(lazy). +-export([ints_from/1]). +ints_from(N) -> + fun() -> + [N|ints_from(N+1)] + end. </code> + <p>Then we can proceed as follows:</p> + <code type="none"><![CDATA[ + +24> XX = lazy:ints_from(1). +#Fun<lazy> +25> XX(). +[1|#Fun<lazy>] +26> hd(XX()). +1 +27> Y = tl(XX()). +#Fun<lazy> +28> hd(Y()). +2 ]]></code> + <p>etc. - this is an example of "lazy embedding"</p> + </section> + + <section> + <title>Parsing</title> + <p>The following examples show parsers of the following type:</p> + <pre> +Parser(Toks) -> {ok, Tree, Toks1} | fail </pre> + <p><c>Toks</c> is the list of tokens to be parsed. A successful parse returns <c>{ok, Tree, Toks1}</c>, where <c>Tree</c> is a parse tree and <c>Toks1</c> is a tail of <c>Tree</c> which contains symbols encountered after the structure which was correctly parsed. Otherwise <c>fail</c> is returned.</p> + <p>The example which follows illustrates a simple, functional parser which parses the grammar:</p> + <pre> +(a | b) & (c | d) </pre> + <p>The following code defines a function <c>pconst(X)</c> in the module <c>funparse</c>, which returns a Fun which parses a list of tokens.</p> + <codeinclude file="funparse.erl" tag="%14" type="erl"></codeinclude> + <p>This function can be used as follows:</p> + <pre> +29><input>P1 = funparse:pconst(a).</input> +#Fun<hof> +30> <input>P1([a,b,c]).</input> +{ok,{const,a},[b,c]} +31> <input>P1([x,y,z]).</input> +fail </pre> + <p>Next, we define the two higher order functions <c>pand</c> and <c>por</c> which combine primitive parsers to produce more complex parsers. Firstly <c>pand</c>:</p> + <codeinclude file="funparse.erl" tag="%16" type="erl"></codeinclude> + <p>Given a parser <c>P1</c> for grammar <c>G1</c>, and a parser <c>P2</c> for grammar <c>G2</c>, <c>pand(P1, P2)</c> + returns a parser for the grammar which consists of sequences of tokens which satisfy <c>G1</c> followed by sequences of tokens which satisfy <c>G2</c>.</p> + <p><c>por(P1, P2)</c> returns a parser for the language described by the grammar <c>G1</c> or <c>G2</c>.</p> + <codeinclude file="funparse.erl" tag="%15" type="erl"></codeinclude> + <p>The original problem was to parse the grammar <c><![CDATA[(a | b) & (c | d)]]></c>. The following code addresses this problem: </p> + <codeinclude file="funparse.erl" tag="%13" type="erl"></codeinclude> + <p>The following code adds a parser interface to the grammar:</p> + <codeinclude file="funparse.erl" tag="%12" type="erl"></codeinclude> + <p>We can test this parser as follows:</p> + <pre> + +32> <input>funparse:parse([a,c]).</input> +{ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} +33> <input>funparse:parse([a,d]).</input> +{ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} +34> <input>funparse:parse([b,c]).</input> +{ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} +35> <input>funparse:parse([b,d]).</input> +{ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} +36> <input>funparse:parse([a,b]).</input> +fail </pre> + </section> + </section> +</chapter> + diff --git a/system/doc/extensions/funs1.erl b/system/doc/extensions/funs1.erl new file mode 100644 index 0000000000..b1a3e21525 --- /dev/null +++ b/system/doc/extensions/funs1.erl @@ -0,0 +1,125 @@ +-module(funs1). +-compile(export_all). +-import(lists, [reverse/1]). + +%1 +map(F, [H|T]) -> [F(H)|map(F, T)]; +map(F, []) -> []. +%1 + +%2 +foreach(F, [H|T]) -> + F(H), + foreach(F, T); +foreach(F, []) -> + ok. +%2 +% +%3 +all(Pred, [H|T]) -> + case Pred(H) of + true -> all(Pred, T); + false -> false + end; +all(Pred, []) -> + true. +%3 +%4 +any(Pred, [H|T]) -> + case Pred(H) of + true -> true; + false -> any(Pred, T) + end; +any(Pred, []) -> + false. +%4 +%5 +takewhile(Pred, [H|T]) -> + case Pred(H) of + true -> [H|takewhile(Pred, T)]; + false -> [] + end; +takewhile(Pred, []) -> + []. +%5 +%6 +dropwhile(Pred, [H|T]) -> + case Pred(H) of + true -> dropwhile(Pred, T); + false -> [H|T] + end; +dropwhile(Pred, []) -> + []. +%6 +%7 +splitlist(Pred, L) -> + splitlist(Pred, L, []). + +splitlist(Pred, [H|T], L) -> + case Pred(H) of + true -> splitlist(Pred, T, [H|L]); + false -> {reverse(L), [H|T]} + end; +splitlist(Pred, [], L) -> + {reverse(L), []}. +%7 + +flatmap(F, [Hd|Tail]) -> + F(Hd) ++ flatmap(F, Tail); +flatmap(F, []) -> []. + +%8 +foldl(F, Accu, [Hd|Tail]) -> + foldl(F, F(Hd, Accu), Tail); +foldl(F, Accu, []) -> Accu. +%8 +% +foldr(F, Accu, [Hd|Tail]) -> + F(Hd, foldr(F, Accu, Tail)); +foldr(F, Accu, []) -> Accu. +%9 +filter(F, [H|T]) -> + case F(H) of + true -> [H|filter(F, T)]; + false -> filter(F, T) + end; +filter(F, []) -> []. +%9 +%10 +mapfoldl(F, Accu0, [Hd|Tail]) -> + {R,Accu1} = F(Hd, Accu0), + {Rs,Accu2} = mapfoldl(F, Accu1, Tail), + {[R|Rs], Accu2}; +mapfoldl(F, Accu, []) -> {[], Accu}. +%10 +mapfoldr(F, Accu0, [Hd|Tail]) -> + {Rs,Accu1} = mapfoldr(F, Accu0, Tail), + {R,Accu2} = F(Hd, Accu1), + {[R|Rs],Accu2}; +mapfoldr(F, Accu, []) -> {[], Accu}. +%11 +first(Pred, [H|T]) -> + case Pred(H) of + true -> + {true, H}; + false -> + first(Pred, T) + end; +first(Pred, []) -> + false. +%11 +% +compose(F, G) -> + fun(X) -> + F(G(X)) + end. + +%20 +iterate(N, F) -> + iterate(N, N+1, F). + +iterate(Stop, Stop, _) -> + []; +iterate(N, Stop, Fun) -> + [Fun(N)|iterate(N+1, Stop, Fun)]. +%20 diff --git a/system/doc/extensions/include.xml b/system/doc/extensions/include.xml new file mode 100644 index 0000000000..cd78644b95 --- /dev/null +++ b/system/doc/extensions/include.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1999</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>Includes</title> + <prepared>Arndt Jonasson</prepared> + <docno>1</docno> + <date>99-01-25</date> + <rev>PA1</rev> + <file>include.sgml</file> + </header> + <p>There are two directives which can be used in Erlang source + files to cause the compiler to temporarily read input from another + source. They are typically used to provide macro definitions and + record definitions from header files. It is recommended to use the + file name extension <c>".hrl"</c> for files which are meant to be + included (the 'h' can be read as "header").</p> + <p>When locating header files a list of directory names, the + compiler include path, is used. In short the list contains the current + working directory of the file server, the base name of the compiled + file, and the directories given by the include option, in that order. + See <c>erlc(1)</c> and <c>compile(3)</c> for the details of the + compiler include path.</p> + + <section> + <title>The -include Directive</title> + <p>The first action taken by the <c>-include</c> directive is to + check if the format of the first path component of the specified + filename is <c>$VAR</c>, for some string <c>VAR</c>. If that is the + case, the value of the environment variable <c>VAR</c> as returned by + <c>os:getenv(VAR)</c> is substituted for the first path component. If + <c>os:getenv/1</c> returns <c>false</c>, the first path component is + left as is. If the filename is absolute (possibly after variable + substitution), the header file with that name is included. Otherwise, + the specified header file is searched for in the directories in the + compiler include path, starting with the first directory in the list. + The first file found while traversing the list is included. Examples:</p> + <code type="none"> + -include("my_records.hrl"). + -include("incdir/more_records.hrl"). + -include("/home/users/proj/recs.hrl"). + -include("$PROJ_ROOT/app1/defs.hrl"). </code> + </section> + + <section> + <title>The -include_lib Directive</title> + <p>The <c>-include_lib</c> directive first tries to find the + specified header file using the procedure employed for the + <c>-include</c> directive. If no header file can be found by searching + the compiler include path, the first path component of the specified + filename (possibly after variable substitution) is regarded as the + name of an application, and the directory of the current version of + the application is searched. Example:</p> + <code type="none"> + -include_lib("mnesia/include/mnemosyne.hrl"). </code> + <p>The compiler is instructed to look for the directory where the + current version of the <c>mnesia</c> application is installed, that is + <c>code:lib_dir(mnesia)</c>, and then search the subdirectory + <c>include</c> for the file <c>mnemosyne.hrl</c>.</p> + </section> +</chapter> + diff --git a/system/doc/extensions/list_comprehensions.erl b/system/doc/extensions/list_comprehensions.erl new file mode 100644 index 0000000000..21ac562aa5 --- /dev/null +++ b/system/doc/extensions/list_comprehensions.erl @@ -0,0 +1,118 @@ +-module(t). +-author('[email protected]'). + +%%-export([test/2]). +-compile(export_all). + +%% Odd numbers. + +%%foo(L) -> [ X || X <- L, (X > X-1) == (X /= X-1) ]. + +bar(L) -> [ X || X <- L, integer(X), gt(X, 3) ]. + +bar(L, M) -> [ Y || X <- L, integer(X), gt(X, 3), + Y <- M, float(Y), gt(X, Y) + ]. + +baz(L) -> [ X || X <- L, atom(X) ]. + +buz(L, Min) -> [ X || Min > 3, X <- L, X >= Min ]. + +gt(X, Y) when X > Y -> true; +gt(X, Y) -> false. + +%% Turn a list into a set. +make_set([]) -> []; +make_set([H|T]) -> + [H|[ + Y || Y <- make_set(T), + Y =/= H + ]]. + +%% Return the Pythagorean triangles with sides +%% of total length less than N +pyth(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N), + C <- lists:seq(1,N), + A+B+C =< N, + A*A+B*B == C*C + ]. + +%% Cut the search space a bit.. +pyth2(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N-A+1), + C <- lists:seq(1,N-A-B+2), + A+B+C =< N, + A*A+B*B == C*C ]. + +%% Return the Cartesian product +cp() -> + [ {X,Y} || + X <- a(), + Y <- b() + ]. + +cp(A,B) when list(A),list(B) -> + [ {X,Y} || + X <- A, + Y <- B + ]. + +%a() -> 1/0. +a() -> [a,b]. +b() -> [1,2,3]. + +%% Return all permutations of a list +perms([]) -> [[]]; +perms(L) -> [ [H|T] || H <- L, T <- perms(L--[H]) ]. + +%% Quick sort +sort([X|Xs]) -> + sort([ Y || Y <- Xs, Y < X ]) ++ + [X] ++ + sort([ Y || Y <- Xs, Y >= X ]); +sort([]) -> []. + +%% Vector addition +vecAdd(Xs,Ys) -> + [ X+Y || {X,Y} <- zip(Xs,Ys) ]. + +zip([X|Xs],[Y|Ys]) -> [{X,Y}|zip(Xs,Ys)]; +zip([],[]) -> []. + +qsort([X|Xs]) -> + qsort(lt(X,Xs)) + ++ [X] ++ + qsort(ge(X,Xs)); +qsort([]) -> []. + +lt(X,[H|T]) when X>H -> [H|lt(X,T)]; +lt(X,[_|T]) -> lt(X,T); +lt(_,[]) -> []. + +ge(X,[H|T]) when X=<H -> [H|ge(X,T)]; +ge(X,[_|T]) -> ge(X,T); +ge(_,[]) -> []. + +test(1,N) -> statistics(runtime),test1(N),statistics(runtime); +test(2,N) -> statistics(runtime),test2(N),statistics(runtime); +test(3,N) -> statistics(runtime),test3(N),statistics(runtime). + +test1(0) -> true; +test1(N) -> + sort([21,12,45,1,3,87,55,77,11,20,6,99,91,13,14,15,66,62,69,71,67,82,83,84,87,86,85]), + test1(N-1). + +test2(0) -> true; +test2(N) -> + qsort([21,12,45,1,3,87,55,77,11,20,6,99,91,13,14,15,66,62,69,71,67,82,83,84,87,86,85]), + test2(N-1). + +test3(0) -> true; +test3(N) -> + lists:sort([21,12,45,1,3,87,55,77,11,20,6,99,91,13,14,15,66,62,69,71,67,82,83,84,87,86,85]), + test3(N-1). diff --git a/system/doc/extensions/list_comprehensions.xml b/system/doc/extensions/list_comprehensions.xml new file mode 100644 index 0000000000..30e32da79c --- /dev/null +++ b/system/doc/extensions/list_comprehensions.xml @@ -0,0 +1,205 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</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>List Comprehensions</title> + <prepared>Joe Armstrong</prepared> + <responsible>Bjarne Däcker</responsible> + <docno>1</docno> + <approved>Bjarne DäKer</approved> + <checked></checked> + <date>96-09-10</date> + <rev>PA1</rev> + <file>list_comprehensions.sgml</file> + </header> + <p>List comprehensions are a feature of many modern functional programming languages. Subject to certain rules, they provide a succinct notation for generating elements in a list.</p> + <p>List comprehensions are analogous to set comprehensions + in Zermelo-Frankel set theory and are called ZF expressions in + Miranda. They are analogous to the <c>setof</c> and + <c>findall</c> predicates in Prolog.</p> + <p>List comprehensions are written with the following syntax: + </p> + <code type="none"> + +[Expression || Qualifier1, Qualifier2, ...] </code> + <p><c>Expression</c> is an arbitrary expression, and each <c>Qualifier</c> is either a generator or a filter.</p> + <list type="bulleted"> + <item>A <em>generator</em> written as <c><![CDATA[Pattern <- ListExpr]]></c>. <c>ListExpr</c> must be an expression which evaluates to a list of terms.</item> + <item>A <em>filter</em> is either a predicate or a boolean expression. A predicate is a function which returns <c>true</c> or <c>false</c>.</item> + </list> + + <section> + <title>Examples of List Comprehensions</title> + <p>We start with a simple example:</p> + <code type="none"><![CDATA[ + +> [X || X <- [1,2,a,3,4,b,5,6], X > 3]. +[a,4,b,5,6] ]]></code> + <p>This should be read as follows:</p> + <quote> + <p>The list of X such that X is taken from the list <c>[1,2,a,...]</c> and X is greater than 3.</p> + </quote> + <p>The notation <c><![CDATA[X <- [1,2,a,...]]]></c> is a generator and the expression <c>X > 3</c> is a filter.</p> + <p>An additional filter can be added in order to restrict the result to integers:</p> + <code type="none"><![CDATA[ + +> [X || X <- [1,2,a,3,4,b,5,6], integer(X), X > 3]. +[4,5,6] ]]></code> + <p>Generators can be combined. For example, the Cartesian product of two lists can be written as follows:</p> + <code type="none"><![CDATA[ + +> [{X, Y} || X <- [1,2,3], Y <- [a,b]]. +[{1,a},{1,b},{2,a},{2,b},{3,a},{3,b}] ]]></code> + + <section> + <title>Quick Sort</title> + <p>The well known quick sort routine can be written as follows:</p> + <code type="none"><![CDATA[ + +sort([Pivot|T]) -> + sort([ X || X <- T, X < Pivot]) ++ + [Pivot] ++ + sort([ X || X <- T, X >= Pivot]); +sort([]) -> []. ]]></code> + <p>The expression <c><![CDATA[[X || X <- T, X < Pivot]]]></c> is the list of all elements in <c>T</c>, which are less than <c>Pivot</c>.</p> + <p><c><![CDATA[[X || X <- T, X >= Pivot]]]></c> is the list of all elements in <c>T</c>, which are greater or equal to <c>Pivot</c>.</p> + <p>To sort a list, we isolate the first element in the list and split the list into two sub-lists. The first sub-list contains all elements which are smaller than the first element in the list, the second contains all elements which are greater than or equal to the first element in the list. We then sort the sub-lists and combine the results.</p> + </section> + + <section> + <title>Permutations</title> + <p>The following example generates all permutations of the elements in a list:</p> + <code type="none"><![CDATA[ + +perms([]) -> [[]]; +perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. ]]></code> + <p>We take take <c>H</c> from <c>L</c> in all possible ways. The result is the set of all lists <c>[H|T]</c>, where <c>T</c> is the set of all possible permutations of <c>L</c> with <c>H</c> removed.</p> + <code type="none"> + +> perms([b,u,g]). +[[b,u,g],[b,g,u],[u,b,g],[u,g,b],[g,b,u],[g,u,b]] </code> + </section> + + <section> + <title>Pythagorean Triplets</title> + <p>Pythagorean triplets are sets of integers <c>{A,B,C}</c> such that <c>A**2 + B**2 = C**2</c>.</p> + <p>The function <c>pyth(N)</c> generates a list of all integers <c>{A,B,C}</c> such that <c>A**2 + B**2 = C**2</c> and where the sum of the sides is less than <c>N</c>.</p> + <code type="none"><![CDATA[ + +pyth(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N), + C <- lists:seq(1,N), + A+B+C =< N, + A*A+B*B == C*C + ]. ]]></code> + <pre> + +> <input>pyth(3).</input> +[]. +> <input>pyth(11).</input> +[]. +><input>pyth(12).</input> +[{3,4,5},{4,3,5}] +> <input>pyth(50).</input> +[{3,4,5}, + {4,3,5}, + {5,12,13}, + {6,8,10}, + {8,6,10}, + {8,15,17}, + {9,12,15}, + {12,5,13}, + {12,9,15}, + {12,16,20}, + {15,8,17}, + {16,12,20}]</pre> + <p>The following code reduces the search space and is more efficient:</p> + <code type="none"><![CDATA[ + +pyth1(N) -> + [{A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N-A+1), + C <- lists:seq(1,N-A-B+2), + A+B+C =< N, + A*A+B*B == C*C ]. ]]></code> + </section> + + <section> + <title>Simplifications with List Comprehensions</title> + <p>As an example, list comprehensions can be used to simplify some of the functions in <c>lists.erl</c>:</p> + <code type="none"><![CDATA[ + +append(L) -> [X || L1 <- L, X <- L1]. +map(Fun, L) -> [Fun(X) || X <- L]. +filter(Pred, L) -> [X || X <- L, Pred(X)]. ]]></code> + </section> + </section> + + <section> + <title>Variable Bindings in List Comprehensions</title> + <p>The scope rules for variables which occur in list comprehensions are as follows:</p> + <list type="bulleted"> + <item>all variables which occur in a generator pattern are assumed to be "fresh" variables</item> + <item>any variables which are defined before the list comprehension and which are used in filters have the values they had before the list comprehension</item> + <item>no variables may be exported from a list comprehension.</item> + </list> + <p>As an example of these rules, suppose we want to write the function <c>select</c>, which selects certain elements from a list of tuples. We might write <c><![CDATA[select(X, L) -> [Y || {X, Y} <- L].]]></c> with the intention of extracting all tuples from <c>L</c> where the first item is <c>X</c>.</p> + <p>Compiling this yields the following diagnostic:</p> + <code type="none"> + +./FileName.erl:Line: Warning: variable 'X' shadowed in generate </code> + <p>This diagnostic warns us that the variable <c>X</c> in the pattern is not the same variable as the variable <c>X</c> which occurs in the function head.</p> + <p>Evaluating <c>select</c> yields the following result:</p> + <pre> + +> <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> +[1,2,3,7] </pre> + <p>This result is not what we wanted. To achieve the desired effect we must write <c>select</c> as follows:</p> + <code type="none"><![CDATA[ + +select(X, L) -> [Y || {X1, Y} <- L, X == X1]. ]]></code> + <p>The generator now contains unbound variables and the test has been moved into the filter. This now works as expected:</p> + <pre> + +> <input>select(b,[{a,1},{b,2},{c,3},{b,7}]).</input> +[2,7] </pre> + <p>One consequence of the rules for importing variables into a list comprehensions is that certain pattern matching operations have to be moved into the filters and cannot be written directly in the generators. To illustrate this, do <em>not</em> write as follows:</p> + <code type="none"><![CDATA[ + +f(...) -> + Y = ... + [ Expression || PatternInvolving Y <- Expr, ...] + ... ]]></code> + <p>Instead, write as follows:</p> + <code type="none"><![CDATA[ + +f(...) -> + Y = ... + [ Expression || PatternInvolving Y1 <- Expr, Y == Y1, ...] + ... + ]]></code> + </section> +</chapter> + diff --git a/system/doc/extensions/list_comrehensions.erl b/system/doc/extensions/list_comrehensions.erl new file mode 100644 index 0000000000..f6a23b5dca --- /dev/null +++ b/system/doc/extensions/list_comrehensions.erl @@ -0,0 +1,75 @@ +-module(zf). + +-compile(export_all). + + +%% Odd numbers. + +%%foo(L) -> [ X || X <- L, (X > X-1) == (X /= X-1) ]. + +boo() -> [X||X <- [1,2,a,3,4,b,5,6], X > 3]. +boo1() -> [X||X <- [1,2,a,3,4,b,5,6], integer(X),X > 3]. +boo2() -> [{X,Y} || X <- [1,2,3], Y <- [a,b]]. + +bar(L) -> [ X || X <- L, integer(X), gt(X, 3) ]. + +bar(L, M) -> [ Y || X <- L, integer(X), gt(X, 3), + Y <- M, float(Y), gt(X, Y) + ]. + +baz(L) -> [ X || X <- L, atom(X) ]. + +buz(L, Min) -> [ X || Min > 3, X <- L, X >= Min ]. + +gt(X, Y) when X > Y -> true; +gt(X, Y) -> false. + + +%% Return the Pythagorean triangles with sides +%% of total length less than N +pyth(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N), + C <- lists:seq(1,N), + A+B+C =< N, + A*A+B*B == C*C + ]. + +%% Cut the search space a bit.. +pyth2(N) -> + [ {A,B,C} || + A <- lists:seq(1,N), + B <- lists:seq(1,N-A+1), + C <- lists:seq(1,N-A-B+2), + A+B+C =< N, + A*A+B*B == C*C ]. + +%% Return the Cartesian product + +cp(A,B) -> + [ {X,Y} || + X <- A, + Y <- B + ]. + +%% Return all permutations of a list +perms([]) -> [[]]; +perms(L) -> [ [H|T] || H <- L, T <- perms(L--[H]) ]. + +%% Quick sort +sort([X|Xs]) -> + sort([ Y || Y <- Xs, Y < X ]) ++ + [X] ++ + sort([ Y || Y <- Xs, Y >= X ]); +sort([]) -> []. + +%% append + +append(L) -> [X||L1<-L,X<-L1]. + +map(Fun, L) -> [Fun(X)||X<-L]. + +filter(Pred, L) -> [X||X<-L,Pred(X)]. + +select(X, L) -> [Y || {X1,Y} <- L, X == X1]. diff --git a/system/doc/extensions/macros.xml b/system/doc/extensions/macros.xml new file mode 100644 index 0000000000..feb3de6102 --- /dev/null +++ b/system/doc/extensions/macros.xml @@ -0,0 +1,177 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</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>Macros</title> + <prepared>Joe Armstrong</prepared> + <responsible>Bjarne Däcker</responsible> + <docno>1</docno> + <approved>Bjarne DäKer</approved> + <checked></checked> + <date>96-09-10</date> + <rev>PA1</rev> + <file>macros.sgml</file> + </header> + <p>Macros in Erlang are written with the following syntax:</p> + <code type="none"> + +-define(Const, Replacement). +-define(Fun(Var1, Var2,.., Var), Replacement). </code> + <p>Macros are expanded when the syntax <c>?MacroName</c> is encountered.</p> + <p>Consider the macro definition:</p> + <code type="none"> + +-define(timeout, 200). </code> + <p>The expression <c>?timeout</c>, which can occur anywhere in the code which follows the macro definition, will be replaced by <c>200</c>.</p> + <p>Macros with arguments are written as follows:</p> + <code type="none"> + + -define(macro1(X, Y), {a, X, b, Y}). </code> + <p>This type of macro can be used as follows:</p> + <code type="none"> + +bar(X) -> + ?macro1(a, b), + ?macro1(X, 123) </code> + <p>This expands to:</p> + <code type="none"> + +bar(X) -> + {a,a,b,b}, + {a,X,b,123}. </code> + + <section> + <title>Macros and Tokens</title> + <p>Macro expansion works at a token level. We might define a macro as follows:</p> + <code type="none"> + +-define(macro2(X, Y), {a,X,b,Y). </code> + <p>The replacement value of the macro is not a valid Erlang term because the closing right curly bracket is missing. <c>macro2</c> expands into a sequence of tokens <c>{</c>, <c>a</c>, <c>X</c> which are then pasted into the place where the macro is used.</p> + <p>We might use this macro as follows:</p> + <code type="none"> + +bar() -> + ?macro2(x,y)}. </code> + <p>This will expand into the valid sequence of tokens <c>{a,x,y,b}</c> before being parsed and compiled.</p> + <note> + <p>It is good programming practise to ensure that the replacement text of a macro is a valid Erlang syntactic form.</p> + </note> + </section> + + <section> + <title>Pre-Defined Macros</title> + <p>The following macros are pre-defined:</p> + <taglist> + <tag><c>?MODULE</c>. </tag> + <item>This macro returns the name of the current module. + </item> + <tag><c>?MODULE_STRING</c>. </tag> + <item>This macro returns the name of the current module, as a string.</item> + <tag><c>?FILE</c>.</tag> + <item>This macro returns the current file name.</item> + <tag><c>?LINE</c>.</tag> + <item>This macro returns the current line number.</item> + <tag><c>?MACHINE</c>.</tag> + <item>This macro returns the current machine name, + <c>'BEAM'</c>,</item> + </taglist> + </section> + + <section> + <title>Stringifying Macro Arguments</title> + <p>The construction <c>??Arg</c> for an argument to a macro expands to a + string containing the tokens of the argument, similar to the + <c>#arg</c> stringifying construction in C. This was added in Erlang + 5.0 (OTP R7A).</p> + <p>Example:</p> + <code type="none"> +-define(TESTCALL(Call), io:format("Call ~s: ~w~n", [??Call, Call])). + +?TESTCALL(myfunction(1,2)), +?TESTCALL(you:function(2,1)).</code> + <p>results in</p> + <code type="none"> +io:format("Call ~s: ~w~n",["myfunction ( 1 , 2 )",m:myfunction(1,2)]), +io:format("Call ~s: ~w~n",["you : function ( 2 , 1 )",you:function(2,1)]).</code> + </section> + + <section> + <title>Flow Control in Macros</title> + <p>The following macro directives are supplied:</p> + <taglist> + <tag>-undef(Macro).</tag> + <item>Causes the macro to behave as if it had never been defined.</item> + <tag>-ifdef(Macro).</tag> + <item>Do the following lines if <c>Macro</c> is defined.</item> + <tag>-ifndef(Macro).</tag> + <item>Do the following lines if <c>Macro</c> is not defined.</item> + <tag>-else.</tag> + <item>"else" macro</item> + <tag>-endif.</tag> + <item>"endif" macro.</item> + </taglist> + <p>The conditional macros must be properly nested. They are usually grouped as follows:</p> + <code type="none"> + +-ifdef(debug) +-define(....) +-else +-define(...) +-endif </code> + <p>The following example illustrates this grouping:</p> + <code type="none"> + +-define(debug, true). +-ifdef(debug). +-define(trace(Str, X), io:format("Mod:~w line:~w ~p ~p~n", + [?MODULE,?LINE, Str, X])). +-else. +-define(trace(X, Y), true). +-endif. </code> + <p>Given these definitions, the expression <c>?trace("X=", X).</c> in line 10 of the module <c>foo</c> expands to:</p> + <code type="none"> + +io:format("Mod:~w line:~w ~p ~p~n",[foo,100,"X=",[X]]), </code> + <p>If we remove the <c>-define(debug, true).</c> line, then the same expression expands to <c>true</c>.</p> + </section> + + <section> + <title>A Macro Expansion Utility</title> + <p>The following code can be used to expand a macro and display the result:</p> + <code type="none"> + +-module(mexpand). +-export([file/1]). +-import(lists, [foreach/2]). +file(File) -> + case epp:parse_file(File ++ ".erl", [],[]) of + {ok, L} -> + {ok, Stream} = file:open(File ++ ".out", write), + foreach(fun(X) -> + io:format(Stream,"~s~n",[erl_pp:form(X)]) + end, L), + file:close(Stream) + end.</code> + <p>Alternatively, we can compile the file with the <c>'P'</c> option. <c>compile:file(File, ['P'])</c> produces a list file <c>File.P</c>, in which the result of any macro expansions can be seen.</p> + </section> +</chapter> + diff --git a/system/doc/extensions/make.dep b/system/doc/extensions/make.dep new file mode 100644 index 0000000000..fdac959667 --- /dev/null +++ b/system/doc/extensions/make.dep @@ -0,0 +1,21 @@ +# ---------------------------------------------------- +# >>>> Do not edit this file <<<< +# This file was automaticly generated by +# /home/otp/bin/docdepend +# ---------------------------------------------------- + + +# ---------------------------------------------------- +# TeX files that the DVI file depend on +# ---------------------------------------------------- + +book.dvi: bit_syntax.tex book.tex funs.tex include.tex \ + list_comprehensions.tex macros.tex misc.tex \ + part.tex records.tex + +# ---------------------------------------------------- +# Source inlined when transforming from source to LaTeX +# ---------------------------------------------------- + +funs.tex: fun_test.erl funparse.erl funs1.erl + diff --git a/system/doc/extensions/mexpand.erl b/system/doc/extensions/mexpand.erl new file mode 100644 index 0000000000..261f99da46 --- /dev/null +++ b/system/doc/extensions/mexpand.erl @@ -0,0 +1,16 @@ +-module(mexpand). + +-export([file/1]). + +-import(lists, [foreach/2]). + +file(File) -> + case epp:parse_file(File ++ ".erl", [],[]) of + {ok, L} -> + {ok, Stream} = file:open(File ++ ".out", write), + foreach(fun(X) -> + io:format(Stream,"~s~n", + [erl_pp:form(X)]) + end, L), + file:close(Stream) + end. diff --git a/system/doc/extensions/misc.xml b/system/doc/extensions/misc.xml new file mode 100644 index 0000000000..576f705278 --- /dev/null +++ b/system/doc/extensions/misc.xml @@ -0,0 +1,310 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1999</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>Miscellaneous</title> + <prepared>Arndt Jonasson</prepared> + <docno>1</docno> + <date>99-01-25</date> + <rev>PA1</rev> + <file>misc.sgml</file> + </header> + <p>In this chapter, a number of miscellaneous features of Erlang + are described.</p> + + <section> + <title>Token Syntax</title> + <p>In Erlang 4.8 (OTP R5A) the syntax of Erlang tokens have been + extended to allow the use of the full ISO-8859-1 (Latin-1) character + set. This is noticeable in the following ways:</p> + <list type="bulleted"> + <item>All the Latin-1 printable characters can be used and are shown without + the escape backslash convention.</item> + <item>Atoms and variables can use all Latin-1 letters.</item> + </list> + <p>The new characters from Latin-1 have the following + classifications in Erlang:</p> + <table> + <row> + <cell align="left" valign="middle"><em>Octal</em></cell> + <cell align="left" valign="middle"><em>Decimal</em></cell> + <cell align="left" valign="middle"> </cell> + <cell align="left" valign="middle"><em>Class</em></cell> + </row> + <row> + <cell align="left" valign="middle">200 - 237</cell> + <cell align="left" valign="middle">128 - 159</cell> + <cell align="left" valign="middle"> </cell> + <cell align="left" valign="middle">Control characters</cell> + </row> + <row> + <cell align="left" valign="middle">240 - 277</cell> + <cell align="left" valign="middle">160 - 191</cell> + <cell align="right" valign="middle">- ¿</cell> + <cell align="left" valign="middle">Punctuation characters</cell> + </row> + <row> + <cell align="left" valign="middle">300 - 326</cell> + <cell align="left" valign="middle">192 - 214</cell> + <cell align="center" valign="middle">À - Ö</cell> + <cell align="left" valign="middle">Uppercase letters</cell> + </row> + <row> + <cell align="center" valign="middle">327</cell> + <cell align="center" valign="middle">215</cell> + <cell align="center" valign="middle">×</cell> + <cell align="left" valign="middle">Punctuation character</cell> + </row> + <row> + <cell align="left" valign="middle">330 - 336</cell> + <cell align="left" valign="middle">216 - 222</cell> + <cell align="center" valign="middle">Ø - Þ</cell> + <cell align="left" valign="middle">Uppercase letters</cell> + </row> + <row> + <cell align="left" valign="middle">337 - 366</cell> + <cell align="left" valign="middle">223 - 246</cell> + <cell align="center" valign="middle">ß - ö</cell> + <cell align="left" valign="middle">Lowercase letters</cell> + </row> + <row> + <cell align="center" valign="middle">367</cell> + <cell align="center" valign="middle">247</cell> + <cell align="center" valign="middle">÷</cell> + <cell align="left" valign="middle">Punctuation character</cell> + </row> + <row> + <cell align="left" valign="middle">370 - 377</cell> + <cell align="left" valign="middle">248 - 255</cell> + <cell align="center" valign="middle">ø - ÿ</cell> + <cell align="left" valign="middle">Lowercase letters</cell> + </row> + <tcaption>Character classes</tcaption> + </table> + </section> + + <section> + <title>String Concatenation</title> + <p>Two adjacent string literals are concatenated into one. This is done already + at compile-time, and doesn't incur any runtime overhead. Example:</p> + <code type="none"> + "string" "42" </code> + <p>is equivalent to</p> + <code type="none"> + "string42" </code> + <p>This feature is convenient in at least two situations:</p> + <list type="bulleted"> + <item>when one of the + strings is the result of a macro expansion;</item> + <item>when a string is very + long, and would otherwise either have to wrap, making the source code + harder to read, or force the use of some runtime append operation.</item> + </list> + </section> + + <section> + <title>The ++ list Concatenation Operator</title> + <p>Since list concatenation is a very common operation, it is convenient + to have a terse way of expressing it. The ++ operator appends its second + argument to its first. Example: + </p> + <code type="none"> + X = [1,2,3], + Y = [4,5], + X ++ Y. </code> + <p>results in <c>[1,2,3,4,5]</c>.</p> + <p>The ++ operator has precedence between the binary '+' operator and + the comparison operators. + </p> + </section> + + <section> + <title>The - - list Subtraction Operator</title> + <p>The - - operator produces a list which is a copy of the first + argument, subjected to the following procedure: for each element in + the second argument, its first occurrence in the first argument is + removed.</p> + <code type="none"> + X = [1,2,3,2,1,2], + Y = [2,1,2], + X -- Y. </code> + <p>results in <c>[3,1,2]</c>.</p> + <p>The - - operator has precedence between the binary '+' operator and + the comparison operators. + </p> + </section> + + <section> + <title>Bitwise Operator bnot</title> + <p>Apart from the binary bitwise operators <c>band</c>, <c>bor</c> + and <c>bxor</c>, there is a unary operator <c>bnot</c> with the same + precedence as the other unary operators + and -, i.e., higher than + the binary operators. Example:</p> + <code type="none"> + bnot 7. </code> + <p>returns -8. + </p> + </section> + + <section> + <title>Logical Operators</title> + <p>The atoms <c>true</c> and <c>false</c> are usually used for representing + Boolean values. With the binary operators <c>and</c>, <c>or</c> and + <c>xor</c>, and the unary operator <c>not</c>, Boolean values can be + combined. Example:</p> + <code type="none"> + + M1 = lists:member(A, List1), + M2 = lists:member(A, List2), + M1 and M2.</code> + <p>Note that the operators are strict, i.e., they always evaluate both + their arguments.</p> + <p><c>not</c> has the same priority as the other unary operators. The + binary logical operators have precedence between the <c>=</c> operator + and the comparison operators, the <c>and</c> operator having higher + precedence than <c>or</c> and <c>xor</c>. + </p> + </section> + + <section> + <title>Match Operator = In Patterns</title> + <p>This extension was added in Erlang 4.8 (OTP R5A).</p> + <p>The = operator is also called the `match' operator. The match operator + can now be used in a pattern, so that <c>P1 = P2</c> is a valid pattern, + where both <c>P1</c> and <c>P2</c> are patterns. This compound pattern + when matched against a term causes the term to be matched against both + <c>P1</c> and <c>P2</c>.</p> + <p>One use for this construction is to avoid reconstructing a term which + was part of an argument to a function. Example:</p> + <code type="none"> + f({'+',X,Y}=T) -> {X+Y,T}.</code> + <p>It also makes it possible to rewrite the construction</p> + <code type="none"> + f(X) when X == #rec{x=1, y=a} -> ... </code> + <p>as</p> + <code type="none"> + f(#rec{x=1, y=a} = X) -> ... </code> + <p>In the absence of optimization for the former case, the + latter case is more efficient. + </p> + </section> + + <section> + <title>Literal String Prefix in Patterns</title> + <p>This extension was added in Erlang 4.8 (OTP R5A).</p> + <p>A new construction is allowed in patterns, namely a literal + string as the first operand of the ++ operator. Example:</p> + <code type="none"> + f("prefix" ++ L) -> ... </code> + <p>This is syntactic sugar for the equivalent, but harder to read</p> + <code type="none"> + f([$p,$r,$e,$f,$i,$x | L]) -> ... </code> + </section> + + <section> + <title>Disjunctions in Guards</title> + <p>This extension was added in Erlang 4.9 (OTP R6A).</p> + <p>A new construction is allowed in guards, the disjunction operator + ';'. The construction is syntactic sugar which removes the bother of + writing the same body after several guards.</p> + <code type="none"> + f(X) when xxx ; yyy ; zzz -> + pop(X).</code> + <p>This is syntactic sugar for the equivalent</p> + <code type="none"> + f(X) when xxx -> + pop(X); + f(X) when yyy -> + pop(X); + f(X) when zzz -> + pop(X). </code> + <p>The abstract format has been changed accordingly to contain a list of + (conjunctive) guards where there was previously only one guard. + </p> + </section> + + <section> + <title>Expressions in Patterns</title> + <p>This extension was added in Erlang 5.0 (OTP R7A).</p> + <p>An arithmetic expression can be used within a pattern, if it uses + only numeric or bitwise operators, and if its value can be evaluated + to a constant at compile-time. This is especially useful when the + expression is defined by a macro. + </p> + <p>Example:</p> + <code type="none"> + case X of + {1+2, T} -> T + end.</code> + </section> + + <section> + <title>Boolean expresions in guards</title> + <p>This extension was added in Erlang 5.1 (OTP R8).</p> + <p>In guards, the use of <c>and</c>, <c>or</c> and <c>not</c> is + now allowed. Guard expressions can combine these with + parenthesis. This allows for more elaborate guards than what + may be given with <c>,</c> and <c>;</c>.</p> + <note> + <p>The guard expressions written with these operators are boolean + expressions, and the boolean functions <c>is_list</c>, + <c>is_integer</c> etc. should be used, rather than + <c>list</c>, <c>integer</c> etc.</p> + </note> + <p>Example 1:</p> + <code type="none"> + f(X) when not (is_tuple(X) or is_list(X)) -> + ... </code> + <p>Example 2:</p> + <code type="none"><![CDATA[ + g(A, B) when (A > 0) and (B > 0) and not (A*A < B*B) -> + ... ]]></code> + </section> + + <section> + <title>Short-circuit boolean expressions</title> + <p>This extension was added in Erlang 5.1 (OTP R8).</p> + <p>In a boolean expression it is unnecessary to always evaluate all + terms. If the first term gives a result that determines the + result, the second term is not needed. In Erlang two new + keywords handles boolean expressions without evaluating both + terms, if it's unnecessary. (This is called short-curcuit + boolean evaluation.)</p> + <p>The keyword <c>andalso</c> is a short-curcuit version of + <c>and</c>. The keyword <c>orelse</c> is a short-curcuit version + of <c>or</c>. They can be used in boolean expressions (not + guards) instead of <c>and</c> and <c>or</c>.</p> + <p>Example 1:</p> + <code type="none"> + case A >= -1.0 andalso math:sqrt(A+1) > B of </code> + <p>This will work even if <c>A</c> is less than <c>-1.0</c>, since + in that case, the second term (after <c>andalso</c>) is never + evaluated. (Of course, the same effects could have been done + using guards. In guards, evaluation is always short-circuited, + since guard tests are known to be free of side-effects.)</p> + <p>Example 2:</p> + <code type="none"> + OnlyOne = is_atom(L) orelse + (is_list(L) andalso length(L) == 1), </code> + </section> +</chapter> + diff --git a/system/doc/extensions/part.xml b/system/doc/extensions/part.xml new file mode 100644 index 0000000000..56fd2c09a6 --- /dev/null +++ b/system/doc/extensions/part.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part> + <header> + <copyright> + <year>1997</year> + <year>2007</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Erlang Extensions Since 4.4</title> + <prepared>OTP Team</prepared> + <docno></docno> + <date>1997-02-21</date> + <rev>E</rev> + <file>part.sgml</file> + </header> + <description> + <p>This chapter describes extensions made to the Erlang language + since version 4.4 (where nothing is said to the contrary, an extension + was added in version 4.4). + The chapter contains the following sections: + </p> + <list type="bulleted"> + <item>Records</item> + <item>Functional Objects (Funs)</item> + <item>List Comprehensions</item> + <item>Macros</item> + <item>File inclusion</item> + <item>Bit syntax</item> + <item>Miscellaneous</item> + </list> + </description> + <include file="records"></include> + <include file="funs"></include> + <include file="list_comprehensions"></include> + <include file="macros"></include> + <include file="include"></include> + <include file="bit_syntax"></include> + <include file="misc"></include> +</part> + diff --git a/system/doc/extensions/records.xml b/system/doc/extensions/records.xml new file mode 100644 index 0000000000..21ec73ab77 --- /dev/null +++ b/system/doc/extensions/records.xml @@ -0,0 +1,284 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year> + <year>2007</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. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Records</title> + <prepared>Joe Armstrong</prepared> + <responsible>Bjarne Däcker</responsible> + <docno>1</docno> + <approved>Bjarne DäKer</approved> + <checked></checked> + <date>96-09-10</date> + <rev>PA1</rev> + <file>records.sgml</file> + </header> + <p>A record is a data structure intended for storing a fixed number of related data items. It is + similar to a <c>struct</c> in C, or a <c>record</c> in Pascal. </p> + <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 <em>tuple</em><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><em>Records</em> 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>In the following sections we describe the different operations which can be performed on records:</p> + + <section> + <title>Defining a Record</title> + <p>A record is defined with the following syntax:</p> + <code type="none"> + +-record(RecordName, {Field1 [= DefaultValue1], + Field2 [= DefaultValue2], + ..., + FieldN [= DefaultValueN]}). </code> + <p>The record name and field names must be atoms. The optional default values, which are terms, are used if no value is supplied for a field when a new instance of the record is created. If the default value is not supplied, then the atom <c>undefined</c> is assumed.</p> + <p>For example, in the following record definition, the address field is <c>undefined</c>.</p> + <pre> +-record(person, {name = "", phone = [], address}). </pre> + <p>This definition of a person will be used in many of the examples which follow.</p> + </section> + + <section> + <title>Including a Record Definition</title> + <p>If the record is used in several modules, its definition should be placed in a <c>.hrl</c> header file. Each module which uses the record definition should have a <c>-include(FileName).</c> statement. For example:</p> + <code type="none"> + +-include("my_data_structures.hrl"). </code> + <note> + <p>The definition of the record must come before it is used.</p> + </note> + </section> + + <section> + <title>Creating a Record</title> + <p>A new record is created with the following syntax:</p> + <code type="none"> + +#RecordName{Field1=Expr1, + ..., + FieldM=ExprM}. </code> + <p>If any of the fields is omitted, then the default value supplied in the record definition is used. For example:</p> + <pre> +> #person{phone = [0,8,2,3,4,3,1,2], name = "Robert"}. +{person, "Robert", [0,8,2,3,4,3,1,2], undefined}. </pre> + <p>There is a new feature introduced in Erlang 5.1 (OTP release + R8), 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> +> #person{name = "Jakob", _ = '_'} +{person, "Jakob", '_', '_'} </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>Selectors</title> + <p>The following syntax is used to select an individual field from a record:</p> + <code type="none"> + +Variable#RecordName.Field </code> + <note> + <p>The values contained in record names and fields must be constants, not variables.</p> + </note> + <note> + <p>For the purposes of illustration, we will demonstrate the use of records using an imaginary dialogue with the Erlang shell. Currently the Erlang evaluator does not support records so you may not be able to reproduce this dialogue.</p> + </note> + <pre> + +> <input>P = #person{name = "Joe", phone = [0,8,2,3,4,3,1,2]}.</input> +{person, "Joe", [0,8,2,3,4,3,1,2], undefined} +> <input>P#person.name.</input> +"Joe" </pre> + <note> + <p>Selectors for records are allowed in guards.</p> + </note> + </section> + + <section> + <title>Updating a Record</title> + <p>The following syntax is used to create a new copy of the record with some of the fields changed. Only the fields to be changed need to be referred to, all other fields retain their old values.</p> + <code type="none"> + +OldVariable#RecordName{Field1 = NewValue1, + ..., + FieldM = NewValueM} </code> + <p>For example:</p> + <pre> +> P1 = #person{name="Joe", phone=[1,2,3], address="A street"}. +{person, "Joe", [1,2,3], "A street"} +> P2 = P1#person{name="Robert"}. +{person, "Robert", [1,2,3], "A street"} </pre> + </section> + + <section> + <title>Type Testing</title> + <p>The following guard test is used to test the type of a record:</p> + <code type="none"> + +record(Variable, RecordName) </code> + <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 record(P, person) -> a_person; +foo(_) -> not_a_person. </pre> + <note> + <p>This test checks that <c>P</c> is a tuple of arity <c>N + 1</c>, where <c>N</c> is the number + of fields in the record, and the first element in the tuple is the atom <c>person</c>.</p> + </note> + </section> + + <section> + <title>Pattern Matching</title> + <p>Matching can be used in combination with records as shown in the following example:</p> + <pre> +> P = #person{name="Joe", phone=[0,0,7], address="A street"}. +{person, "Joe", [0,0,7], "A street"} +> #person{name = Name} = P, Name. +"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> + <note> + <p>The fields referred to in the pattern can be given in any order.</p> + </note> + </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> + <note> + <p>In this example, <c>demo()</c> evaluates to <c>"Robert"</c>.</p> + </note> + </section> + + <section> + <title>Internal Representation of Records</title> + <p>It is often desirable to write generic functions which will work on any record, not just a record of a particular type. For this reason, records are represented internally as tuples and the ordering of the fields in the tuple is strictly defined.</p> + <p>For example, the record <c>-record(person, {name, phone, address}).</c> is represented internally by the tuple <c>{person, X, Y, Z}</c>.</p> + <p>The arity of the tuple is one more than the number of fields in the tuple. The first element of the tuple is the name of the record, and the elements of the tuple are the fields in the record. The variables <c>X</c>, <c>Y</c> and <c>Z</c> will store the data contained in the record fields.</p> + <p>The following two functions determine the indices in the tuple which refer to the named fields in the record:</p> + <list type="bulleted"> + <item><c>record_info(fields, Rec) -> [Names]</c>. This function returns the names of the fields in the record <c>Rec</c>. For example, <c>record_info(fields, person)</c> evaluates to <c>[name, address, phone]</c>.</item> + <item><c>record_info(size, Rec) -> Size</c>. This function returns the size of the record <c>Rec</c> when represented as a tuple, which is one more than the number of fields. For example, <c>record_info(size, person)</c> returns <c>4</c>.</item> + </list> + <p>In addition, <c>#Rec.Name</c> returns the index in the tuple representation of <c>Name</c> of the record <c>Rec</c>.</p> + <note> + <p><c>Name</c> must be an atom.</p> + </note> + <p>For example, the following test function <c>test()</c> might return the result shown:</p> + <pre> +test() -> + {record_info(fields, person), + record_info(size, person), + #person.name}. </pre> + <pre> +> <input>Mod:test().</input> +{[name,address,phone],4,2} </pre> + <p>The order in which records map onto tuples is implementation dependent.</p> + <note> + <p><c>record_info</c> is a pseudo-function which cannot be exported from the module where it occurs.</p> + </note> + </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> + diff --git a/system/doc/extensions/warning.gif b/system/doc/extensions/warning.gif Binary files differnew file mode 100644 index 0000000000..96af52360e --- /dev/null +++ b/system/doc/extensions/warning.gif |