Skip to content

Provide defguard #2469

Closed
Closed
@josevalim

Description

@josevalim

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?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions