Avoid newbie record redundancy?

Raimo Niskanen raimo@REDACTED
Mon May 29 09:41:41 CEST 2006


I also prefer this pattern-matching approach.

About efficiency: If you have several patterns, the language
defines that the first pattern that matches will be picked,
but the compiler optimizes the tests an reorders them to
find an efficient match test sequence:
        case X of
                {a,b,c,d} -> 4;
                {a,b,c,_} -> 3;
                {a,b,_,_} -> 2;
                {a,_,_,_} -> 1;
                {_,_,_,_} -> 0;
                _         -> oops
        end
if the first pattern does not match, the pattern matching
code knows if the first fields are 'a', 'b', etc or not
and does not retest them. The pattern is compiled into
a testing code a'la:
        if is_tuple(X) and is_arity(4, X) ->
          if element(1, X) == a ->
            if element(2, X) == b ->
              if element(3, X) == c ->
                if element(4, X) == d ->
                  4;
                else ->
                  3;
              else ->
                2;
            else ->
              1;
          else ->
            0;
        else
          error({badmatch,X})

But if a guard is thrown in at an unlucky position:
        case X of
                {a,b,c,d} -> 4;
                {a,b,c,_} -> 3;
                _ when is_integer(X) -> int;
                {a,b,_,_} -> 2;
                {a,_,_,_} -> 1;
                {_,_,_,_} -> 0;
                _         -> oops
        end
the compiler is not smart enough to analyze the guard
expression, it just splits the pattern matching into
two sections, before or after the guard, and optimizes
them separately. So in this example after the is_integer(X)
guard it restarts testing if X is a tuple, if arity is 4,
if element 1 is 'a', ...

So here you can help the compiler by moving the
is_integer() guard down to just before the _ pattern.

Otherwise I do not think guard tests are partically
slow. They are calls to guard BIFs, which is a wee
bit slower than pattern matching code that is 
specalized instructions, but the standard pitfall
is mixing guards with pattern matching in 
unlucky combinations as I described above.



> Hi,
> 
> On Sat, May 27, 2006 at 01:34:01PM -0700, Damien Katz wrote:
> > Is the following also acceptable, both functionally
> > and stylewise? I find this more readable, but most
> > code I see written uses the when clause and
> > is_record().
> > 
> > match_queue(MasterQueue) ->
> >  receive
> >    { msgPeerMaster, #rcdPeerMaster{} = A } ->
> >       match_queue(queue:in(A, MasterQueue));
> >    _ ->
> >       ?elog("HELP!~n", [])
> >     end.
> 
> I think both ways of doing it are functionally equivalent in this case.
> Besides, with your approach, you could pattern match directly parts of the
> record, so it is potentially more powerful: 
> 
>      #rcdPeerMaster{field1 = Value1, field2 = Value2, ...} = A.
> 
> Stylewise I also think the one you propose is nicer, but I it is very
> subjective.
> 
> BTW, if I remember correctly, I once read somewhere that using guards is
> less efficient than pattern matching directly using the record type. Anyone
> could confirm this?
> 
> Cheers.
> 

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list