Closed
Description
Today implementing a guard is somehow complicated as we need to quote/unquote arguments depending if the function is being invoked inside or outside of a guard. Here is an example from the Record module:
defmacro record?(data, kind) do
case Macro.Env.in_guard?(__CALLER__) do
true ->
quote do
is_tuple(unquote(data)) and tuple_size(unquote(data)) > 0
and :erlang.element(1, unquote(data)) == unquote(kind)
end
false ->
quote do
result = unquote(data)
is_tuple(result) and tuple_size(result) > 0
and :erlang.element(1, result) == unquote(kind)
end
end
end
The idea is to provide a defguard(p)
macro that does all the work automatically and assert the expressions in the guard are valid. The example above would be rewritten as:
defguard record?(data, kind) do
is_tuple(data) and tuple_size(data) > 0 and elem(data, 0) == kind
end
defguard(p)
is extremely limited in scope:
- the function head cannot pattern match
- code blocks are not allowed in guards
- no assignment
- no local functions
Since we have all limitations above, it is debatable if defguard
should use the do/end
syntax as it "promotes" code blocks. Here are other alternatives:
defguard record?(data, kind) =
is_tuple(data) and tuple_size(data) > 0 and elem(data, 0) == kind
or:
defguard record?(data, kind),
is_tuple(data) and tuple_size(data) > 0 and elem(data, 0) == kind
or even:
defguard record?(data, kind) when
is_tuple(data) and tuple_size(data) > 0 and elem(data, 0) == kind
Thoughts?