Reasoning Behind Illegal If Guard Expression?

I am writing a function that will push a value to a list on a map if the key already exists, or init the key on the map with a new list (containing the pushed element) if it does not already exist.

I tried making this part of the function guard expression but adding this guard expression to the function was illegal. I then tried making this an if but once again the guard expression was illegal. Finally I used a case which was fine.

I believe I’ve read that only a handful of specific BIFs are allowed in function guard expressions, but why is this BIF in the if statement also illegal?

If:

map_push(M, R, V) ->
  if
    maps:is_key(R, M) == true -> #{R := Row} = M, M#{R := [V|Row]};
    maps:is_key(R, M) == false -> #{R => [V]}
  end.

---

illegal guard expression
%   42|     maps:is_key(R, M) == false -> #{R => [V]}
%     |     ^

Case:

map_push(M, R, V) ->
  case maps:is_key(R, M) of
    true -> #{R := Row} = M, M#{R := [V|Row]};
    false -> #{R => [V]}
  end.

I think the case is preferred here anyway, but I’d like to understand why this illegal in the if.

3 Likes

What you’re after is in fact is_map_key/2 , maps:is_key/2 is not allowed in guard expressions.

In the above you’re not using maps:is_key/2 as a guard, you’re matching with guards on the result of the call.

I would agree case expressions are preferred over if, but I also advise the use of function heads before resorting to a case expression in general. Thus :

map_push(M, R, V) when is_map_key(R, M) -> 
   #{R := Row} = M, 
   M#{R := [V|Row]};

map_push(_M, R, V) -> 
   #{R => [V]}.

Edit:

Here’s a list of bifs for use in guard expressions : Erlang -- Expressions

3 Likes

Thank you for your response and link to the list of accepted guard BIFs.

I also found this explanation of “side effects” which consider errors as a side effect: [erlang-questions] Side-effects in "if" guards

3 Likes