About the usage of “if” in Erlang language

2019-02-25 10:28发布

问题:

This is my code:

    lists:foreach(fun(Method, Value)->
                      ServerName = method_to_servername(Method),
                      if
                          Value ==0 andalso whereis(ServerName) =/= undefined ->
                              supervisor:terminate_child(flowrate, whereis(ServerName));
                          Value =/= 0 andalso whereis(ServerName) == undefined ->
                              supervisor:start_child(?MODULE, [Method]);
                          Value =/=0 andalso whereis(ServerName) =/= undefined ->
                              gen_server:call(method_to_servername(Method),
                                              {update_config,
                                               {DesAddress, Method, RateLimitList,
                                                QueueTime,
                                                MinRetry, MaxRetry, Callback}} );
                          true -> ok
                      end
              end, ?ALL_METHODS).

when i compile the code, i meet this problem :illegal guard expression, can you give me some advise.

回答1:

The tests in an if expression are called guard sequences. Only a limited number of functions are allowed in guard sequences, and whereis is not one of them. See the section on Guard Sequences in the Erlang Reference Manual for the complete list.

As a consequence, most Erlang programmers rarely use if. Using case often gives more natural and concise code. Your example could be written as:

lists:foreach(fun(Method, Value)->
                  ServerName = method_to_servername(Method),
                  case {Value, whereis(ServerName)} of
                      {0, ServerPid} when is_pid(ServerPid) ->
                          supervisor:terminate_child(flowrate, ServerPid);
                      {_, undefined} when Value =/= 0 ->
                          supervisor:start_child(?MODULE, [Method]);
                      {_, ServerPid} when is_pid(ServerPid) ->
                          gen_server:call(method_to_servername(Method),
                                          {update_config,
                                           {DesAddress, Method, RateLimitList,
                                            QueueTime,
                                            MinRetry, MaxRetry, Callback}} );
                      _ -> ok
                  end
          end, ?ALL_METHODS).


回答2:

With this code you have several issues:

  1. lists:foreach/2 requires fun with arity one as first argument, you have with two. I guess you have ?ALL_METHODS macros defined similar to this: -define(ALL_METHODS, [{method1, 1}, {method2, 2}, ...]) then you could fix it with wrapping fun's arguments in tuple: fun(Method, Value) will be fun({Method, Value})
  2. With whereis(ServerName) =/= undefined you have possible race condition, when whereis(ServerName) return pid() at first, then process for some reason dies and on the next step it will be undefined (but it is hard to say 100% sure without context).
  3. You do method_to_servername(Method) twice.

And about your question, guard functions in Erlang do allow only certain subset of functions.