Roger Johansson
Roger Johansson

Reputation: 23214

Erlang function overloads

I've just begun fiddling with Erlang. I've seen a lot of examples where pattern matching is used on function declarations, like so:

factorCount (N) ->
   Sqrt = math:sqrt (N),
   ISqrt = trunc(Sqrt),
   if ISqrt == Sqrt -> factorCount (N, ISqrt, 1, -1);
      true          -> factorCount (N, ISqrt, 1, 0)
   end.

factorCount (_N, ISqrt, Candidate, Count) when Candidate > ISqrt -> Count;
factorCount ( N, ISqrt, Candidate, Count) ->
    case N rem Candidate of
        0 -> factorCount (N, ISqrt, Candidate + 1, Count + 2);
        _ -> factorCount (N, ISqrt, Candidate + 1, Count)
    end.

Why is it done this way?. e.g.

factorCount (_N, ISqrt, Candidate, Count) when Candidate > ISqrt -> Count;
factorCount ( N, ISqrt, Candidate, Count) ->

Why is that not just one function with an internal condition inside of it?

Upvotes: 3

Views: 1102

Answers (2)

Hynek -Pichi- Vychodil
Hynek -Pichi- Vychodil

Reputation: 26121

Besides Steve's answer, it will be more obvious if I rewrite your code:

factorCount(_N, ISqrt, Candidate, Count)
  when Candidate > ISqrt ->
    Count;
factorCount(N, ISqrt, Candidate, Count)
  when N rem Candidate =:= 0 ->
    factorCount(N, ISqrt, Candidate + 1, Count + 2);
factorCount(N, ISqrt, Candidate, Count) ->
    factorCount(N, ISqrt, Candidate + 1, Count).

It become more obvious in code where you combine pattern matching with some guards:

merge([{K, V1} | T1], [{K, V2} | T2]) ->
    [{K, [V1, V2]} | merge(T1, T2)];
merge([{K1, V1} | T1], [{K2, _} | _] = L2)
  when K1 < K2 ->
    [{K1, [V1]} | merge(T1, L2)];
merge(L1, [{K2, V2} | T2]) ->
    [{K2, [V2]} | merge(L1, T2)];
merge(L1, []) ->
    [{K, [V]} || {K, V} <- L1].

It would not make much sense if I combine the second and the third function clauses together just to make K1 < K2 comparison inside of the function clause.

Upvotes: 3

Steve Vinoski
Steve Vinoski

Reputation: 20014

One reason is for readability and maintainability. Conditions inside functions tend to make the code creep to the right due to additional indentation, and that combined with the extra conditions in the body make for longer functions that are harder to read, reason about, and maintain.

Another reason is that functions often have preconditions, and guards can often help express those preconditions more clearly than code embedded within the function body.

Still aother reason is that pattern matching the arguments always occurs when functions are called, since that's essentially how function arguments get their bindings, so it's natural to then make use of those bound values in guards. Guards are a kind of extension to pattern matching, allowing you to check things that pattern matching alone can't check.

Upvotes: 5

Related Questions