Erlang style - case vs function pattern matching

2019-02-05 20:39发布

I've got to the stage where I've written quite a bit of Erlang code now, and I can see some style (bad or good) creeping into the way I've been writing it. This particular idiom I'd like some opinion on - is it better (more readable/faster/whatever) to convert case style statements to function pattern matching?

E.g.

Compare (a contrived example)

case {Size > 100000, Type} of
    {true, ets } ->
         %% Do something to convert to dets
         something;
    {false, dets} ->
         %% do something to convert to ets
         somethingelse;
    _ ->
         ignoreit
end;

with

...
maybeChangeStorage(Size, Type)
...

maybeChangeStorage(Size, ets) when Size > 10000 ->
   something;
maybeChangeStorage(Size, dets) when Size < 10000 ->
   somethingelse;
maybeChangeStorage(_,_) ->
   ignoreit.

I prefer the latter in most cases but I'd be interested in other opinion.

6条回答
Evening l夕情丶
2楼-- · 2019-02-05 21:01

If in your function the first thing you do is open a case clause, it is better to convert this top level clause to function pattern matching.

查看更多
Bombasti
3楼-- · 2019-02-05 21:03

The second is the preferred way especially if you can keep the clauses to a single line:

maybeCngStor(Sz, ets)  when Sz > 10000 -> something;
maybeCngStor(Sz, dets) when Sz < 10000 -> somethingelse;
maybeCngStor(_,_)                      -> ignoreit.

Makes it very easy to read and reason about. Always choose the style that will be easiest to read in the future. Often you find a set of clauses where one is a 10 liner and the rest are one lines only - break out the long one to a function:

maybeCngStor(Sz, ets)  when Sz > 10000 -> something;
maybeCngStor(Sz, dets) when Sz < 10000 -> somethingelse();
maybeCngStor(_,_)                      -> ignoreit.

somethingelse() ->
   (...)
   Return.

Little things like laying out the clauses to align them and using short variable names matter - but don't fall into the trap of changing everything to P, Q, R.

A good trick if you use records a lot is to match out the records to short variables:

#record{foo = F, bar = B, baz = Bz} = Parameter

This gives you short variable names that make sense when you parachute into the function from 10,000 feet looking for a bug next Christmas. F obviously is a Foo, etc, etc...

查看更多
Juvenile、少年°
4楼-- · 2019-02-05 21:13

You can make these examples more similar by doing:

case Type of
   ets  when Size > 10000 -> ...;
   dets when Size < 10000 -> ...;
   _ -> ...
end.

This seems to be clearer to me. The advantage of splitting this to a separate function is that you get to give it a name which acts as documentation and appears in stack traces. If that snippet is part of a larger function I'd separate it out, otherwise it's okay as is.

One thing worth considering is that error case as written the function will accept Type arguments other than ets/dets. Unless this is really what you want it's worth making this clause more restrictive.

查看更多
smile是对你的礼貌
5楼-- · 2019-02-05 21:17

As for me first style is more clear and may be faster. But it need test to say it exactly. In second case if type!=ets then both "Size > 10000" and "Size < 10000" would be evaluated.

查看更多
地球回转人心会变
6楼-- · 2019-02-05 21:21

Learn you some Erlang for great good has a small section on when to choose case and when to use a function. Two things are mentioned:

  1. They are represented in the same way in the VM so there is no difference in performance between the two solutions.

  2. If you need to use guards against more than one arguments, using a function may read better.

All in all, it is mostly a question of style and taste.

查看更多
淡お忘
7楼-- · 2019-02-05 21:26

(Put as an answer to get the formatting of the code...!)

One thing I did find when I was making some changes is that this approach can alter default short circuiting. E.g.

case A > 10 of 
      true -> 
             case B > 10 of 
                  true -> dummy1; 
                  false -> dummy2 
             end; 
      false -> dummy3 
end 

would have to always execute B > 10 if you called it like

doTest(A > 10, B > 10) 

when

doTest(true, true) -> dummy1; 
doTest(true, false) -> dummy2; 
doTest(false, _) -> dummy3. 

which sometimes isn't what you want!

查看更多
登录 后发表回答