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.
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.
The concepts of higher order functions and procedural abstraction are introduced with two brief examples.
If we want to double every element in a list, we could write a function named
double([H|T]) -> [2*H|double(T)];
double([]) -> []
This function obviously doubles the argument entered as input as follows:
> double([1,2,3,4]). [2,4,6,8]
We now add the function
add_one([H|T]) -> [H+1|add_one(T)];
add_one([]) -> [].
These functions,
We can now express the functions
double(L) -> map(fun(X) -> 2*X end, L).
add_one(L) -> map(fun(X) -> 1 + X end, L).
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:
This example illustrates procedural abstraction. Initially, we show the following two examples written as conventional functions:
print_list(Stream, [H|T]) ->
io:format(Stream, "~p~n", [H]),
print_list(Stream, T);
print_on_list(Stream, []) ->
true.
broadcast(Msg, [Pid|Pids]) ->
Pid ! Msg,
broadcast(Msg, Pids);
broadcast(_, []) ->
true.
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.
The function
Using
foreach(fun(H) -> io:format(S, "~p~n~,[H]) end, L)
foreach(fun(Pid) -> Pid ! M end, L)
Programming with higher order functions, such as
Funs are written with the syntax:
F = fun (Arg1, Arg2, ... ArgN) ->
...
end
This creates an anonymous function of
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:
F = fun FunctionName/Arity
With this form of function reference, the function which is referred to does not need to be exported from the module.
We can also refer to a function defined in a different module with the following syntax:
F = {Module, FunctionName}
In this case, the function must be exported from the module in question.
The follow program illustrates the different ways of creating Funs:
We can evaluate the fun
F(Arg1, Arg2, ..., Argn)
To check whether a term is a Fun, use the test
f(F, Args) when function(F) ->
apply(F, Args);
f(N, _) when integer(N) ->
N.
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.
In OTP R5 and earlier releases, funs were represented using tuples.
The scope rules for variables which occur in Funs are as follows:
The following examples illustrate these rules:
print_list(File, List) ->
{ok, Stream} = file:open(File, write),
foreach(fun(X) -> io:format(Stream,"~p~n",[X]) end, List),
file:close(Stream).
In the above example, the variable
Since any variable which occurs in the head of a Fun is considered a new variable it would be equally valid to write:
print_list(File, List) ->
{ok, Stream} = file:open(File, write),
foreach(fun(File) ->
io:format(Stream,"~p~n",[File])
end, List),
file:close(Stream).
In this example,
./FileName.erl:Line: Warning: variable 'File'
shadowed in 'lambda head'
This reminds us that the variable
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
f(...) ->
Y = ...
map(fun(X) when X == Y ->
;
(_) ->
...
end, ...)
...
instead of
f(...) ->
Y = ...
map(fun(Y) ->
;
(_) ->
...
end, ...)
...
The following examples show a dialogue with the Erlang shell. All the higher order functions discussed are exported from the module
1> Double = fun(X) -> 2 * X end. #Fun<erl_eval> 2>lists:map(Double, [1,2,3,4,5]). [2,4,6,8,10]
When a new Fun is defined in the shell, the value of the Fun is printed as
We define a predicate
3> Big = fun(X) -> if X > 10 -> true; true -> false end end. #Fun<erl_eval> 4>lists:any(Big, [1,2,3,4]). false. 5> lists:any(Big, [1,2,3,12,5]). true.
6>lists:all(Big, [1,2,3,4,12,6]). false 7>lists:all(Big, [12,13,14,15]). true
8> lists:foreach(fun(X) -> io:format("~w~n",[X]) end, [1,2,3,4]). 1 2 3 4 true
If we have a list of lists
9> L = ["I","like","Erlang"]. ["I","like","Erlang"] 10> lists:foldl(fun(X, Sum) -> length(X) + Sum end, 0, L). 11
L = ["I","like","Erlang"],
Sum = 0,
while( L != []){
Sum += length(head(L)),
L = tail(L)
end
First upcase:
11> Upcase = fun(X) when $a =< X, X =< $z -> X + $A - $a; (X) -> X end. #Fun<erl_eval> 12> Upcase_word = fun(X) -> lists:map(Upcase, X) end. #Fun<erl_eval> 13>Upcase_word("Erlang"). "ERLANG" 14>lists:map(Upcase_word, L). ["I","LIKE","ERLANG"]
Now we can do the fold and the map at the same time:
14> lists:mapfoldl(fun(Word, Sum) -> 14> {Upcase_word(Word), Sum + length(Word)} 14> end, 0, L). {["I","LIKE","ERLANG"],11}
15>lists:filter(Big, [500,12,2,45,6,7]). [500,12,45]
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
diff(L1, L2) ->
filter(fun(X) -> not member(X, L2) end, L1).
The AND intersection of the list
intersection(L1,L2) -> filter(fun(X) -> member(X,L1) end, L2).
16>lists:takewhile(Big, [200,500,45,5,3,45,6]). [200,500,45]
17> lists:dropwhile(Big, [200,500,45,5,3,45,6]). [5,3,45,6]
18>lists:splitlist(Big, [200,500,45,5,3,45,6]). {[200,500,45],[5,3,45,6]}
19>lists:first(Big, [1,2,45,6,123]). {true,45} 20>lists:first(Big, [1,2,4,5]). false
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.
21> Adder = fun(X) -> fun(Y) -> X + Y end end. #Fun<erl_eval> 22> Add6 = Adder(6). #Fun<erl_eval> 23>Add6(10). 16
The idea is to write something like:
-module(lazy).
-export([ints_from/1]).
ints_from(N) ->
fun() ->
[N|ints_from(N+1)]
end.
Then we can proceed as follows:
XX = lazy:ints_from(1).
#Fun
25> XX().
[1|#Fun]
26> hd(XX()).
1
27> Y = tl(XX()).
#Fun
28> hd(Y()).
2 ]]>
etc. - this is an example of "lazy embedding"
The following examples show parsers of the following type:
Parser(Toks) -> {ok, Tree, Toks1} | fail
The example which follows illustrates a simple, functional parser which parses the grammar:
(a | b) & (c | d)
The following code defines a function
This function can be used as follows:
29>P1 = funparse:pconst(a). #Fun<hof> 30> P1([a,b,c]). {ok,{const,a},[b,c]} 31> P1([x,y,z]). fail
Next, we define the two higher order functions
Given a parser
The original problem was to parse the grammar
The following code adds a parser interface to the grammar:
We can test this parser as follows:
32> funparse:parse([a,c]). {ok,{'and',{'or',1,{const,a}},{'or',1,{const,c}}}} 33> funparse:parse([a,d]). {ok,{'and',{'or',1,{const,a}},{'or',2,{const,d}}}} 34> funparse:parse([b,c]). {ok,{'and',{'or',2,{const,b}},{'or',1,{const,c}}}} 35> funparse:parse([b,d]). {ok,{'and',{'or',2,{const,b}},{'or',2,{const,d}}}} 36> funparse:parse([a,b]). fail