[Ericsson AB]

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.

Note!

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.

Different ways of calling a function
Type of call Example Relative cost
(5.4)
Local call foo() 1.00
External call m:foo() 1.08
Fun call Fun = fun(X) -> X + 1 end, Fun(2) 2.79
Apply fun Fun = fun(X) -> X + 1 end, apply(Fun,[2]) 3.54
Implicit apply M:Foo() 7.76
Apply MFA/3 apply(M, Foo, []) 8.21

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.

Note!

The syntax M:Foo(A1,A2,An) (also referred to as implicit apply, where M and Foo are bound variables) where equivalent with apply(M,Foo,[A1,A2,An]) in releases pre OTP-R10B. The compiler will now optimize this syntax giving it better performance than apply/3.

The use of apply/3 for just calling different functions within the same module (i.e apply(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.

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-recursive
    

DO NOT

list_length([]) ->
    0. % Base case
list_length([_ | Tail]) ->
    list_length(Tail) + 1. % Not tail-recursive
    

3.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.
    

Copyright © 1991-2007 Ericsson AB