3 Functions
3.1 Pattern matching
Pattern matching in function, case and receive-clauses are optimized by the compiler. In most cases, there is nothing to gain by rearranging clauses.
3.2 Function Calls
A function can be called in a number of ways and the cost differs a lot. Which kind of call to use depends on the situation. Below follows a table with the available alternatives and their relative cost.
The figures shown as relative cost is highly dependent on the implementation and will vary between versions and platform. The order from lowest to highest cost will however be stable and is very useful to be aware of.
Type of call Example Relative cost
(5.1)Local call foo()
1.00 External call m:foo()
1.07 Fun call Fun = fun(X) -> X + 1 end, Fun(2)
2.52 Apply fun Fun = fun(X) -> X + 1 end, apply(Fun,[2])
3.32 Apply MFA/3 apply(M, Foo, [])
orM:Foo()
7.09 Different ways of calling a function Apply is the most expensive way to call a function and should be avoided in time critical code. A well motivated use of apply is in conjunction with generic interfaces where several modules provide the same set of functions. The use of
apply/3
for just calling different functions within the same module (i.eapply(mymodule,Func,Args)
) is not recommended. The use of Funs can often be a more efficient way to accomplish calls which are variable in runtime.The last entry in the table above shows the syntax
M:Foo(A1,A2,An)
(where M and Foo are bound variables) which is equivalent withapply(M,Foo,[A1,A2,An])
. From an efficiency point of view it is recommended to use theM:Foo(A1,A2,An)
form since this gives the compiler an opportunity to optimize the call in future versions.3.3 Memory usage in recursion
When writing recursive functions it is preferable to make them tail-recursive so that they can execute in a constant memory space.
DO
list_length(List) -> list_length(List, 0). list_length([], AccLen) -> AccLen; % Base case list_length([_|Tail], AccLen) -> list_length(Tail, AccLen + 1). % Tail-recursiveDO NOT
list_length([]) -> 0. % Base case list_length([_ | Tail]) -> list_length(Tail) + 1. % Not tail-recursive3.4 Unnecessary evaluation in each recursive step
Do not evaluate the same expression in each recursive step, rather pass the result around as a parameter. For example imagine that you have the function in_range/3 below and want to write a function in_range/2 that takes a list of integers and atom as argument. The atom specifies a key to the named table range_table, so you can lookup the max and min values for a particular type of range.
in_range(Value, Min, Max) -> (Value >= Min) and (Value =< Max).DO
in_range(ValuList, Type) -> %% Will be evaluated only one time ... [{Min, Max}] = ets:lookup(range_table, Type), %% ... send result as parameter to recursive help-function lists_in_range(ValuList, Min, Max). lists_in_range([Value | Tail], Min, Max) -> case in_range(Value, Min, Max) of true -> lists_in_range(Tail, Min, Max); false -> false end; lists_in_range([], _, _) -> true.DO NOT
in_range([Value | Tail], Type) -> %% Will be evaluated in each recursive step [{Min, Max}] = ets:lookup(range_table, Type), case in_range(Value, Min, Max) of true -> lists_in_range(Tail, Type); false -> false end; in_range([], _, _) -> true.