[Ericsson Utvecklings 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.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, []) or M:Foo() 7.09

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

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 with apply(M,Foo,[A1,A2,An]). From an efficiency point of view it is recommended to use the M: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-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-2002 Ericsson Utvecklings AB