4

The code below shows an example of what I'm asking about: why Map, Enum, etc... can't be used within guard clauses or inside my own macros.

defmodule GuardMod do
    defmacro has_any_key(arg, keys) do
        quote do
            Map.keys(unquote(arg), unquote(keys)) |> Enum.any?
        end
    end
end

defmodule OtherMod do
    import GuardMod

    @doc """
    has_any_key/2 is allowed, but Map.keys or Map.any not
    """
    def fn1(list) when has_any_key(list, [:key1, :key2]), do: :nothing
end
Marcos Costa Pinto
  • 1,676
  • 3
  • 12
  • 14

1 Answers1

7

It's a compromise to allow multi function head dispatching to be "fast".

The type of functions allowed in guards all have a limited runtime or reduction count. If you allow arbitrary functions in guards, you have to deal with the case in which the guard evaluation does not finish before the current process exceeds it current execution slice. ( Or potentially even worse cases a race condition because it requires input from another process).

  • I've read this article http://keathley.io/2016/04/09/elixir-guard-clauses.html that answers my question perfectly and is exactly you said. Guards are limited to some specific functions that ensure "purity" and have no side effects, like race conditions. – Marcos Costa Pinto Jun 12 '17 at 14:04
  • 1
    There is one slow function allowed in guards though, `length/1`. Like the answers [here](https://stackoverflow.com/questions/11177109/can-i-make-my-own-guards-in-erlang) say, the main reason is the functions should be pure and it should be ok to ignore their raised errors. Erlang/Elixir don't allow marking user defined functions as pure / ok to ignore errors, so you can only use the whitelisted functions. – Dogbert Jun 13 '17 at 09:32