Kevin
Kevin

Reputation: 301

Question mark and "is_" in Elixir function names

I'm reading the naming convention for Elixir. It states:

The names of predicate functions that cannot be used within guards should have a trailing question mark (?) rather than the is_ (or similar) prefix.

Hence, what kind of functions can't be used in guard?

Upvotes: 7

Views: 3842

Answers (3)

Guido
Guido

Reputation: 887

From the Elixir 1.7.3 documentation: (bold is mine)

As mentioned before, only the expressions listed in this page are allowed in guards. However, we can take advantage of macros to write custom guards that can simplify our programs or make them more domain-specific. At the end of the day, what matters is that the output of the macros (which is what will be compiled) boils down to a combinations of the allowed expressions.

and from Naming Conventions doc,:

Trailing question mark (foo?) Functions that return a boolean are named with a trailing question mark. Examples: Keyword.keyword?/1, Mix.debug?/0, String.contains?/2 However, functions that return booleans and are valid in guards follow another convention, described next.

is_ prefix (is_foo) Type checks and other boolean checks that are allowed in guard clauses are named with an is_ prefix. Examples: Integer.is_even/1, Kernel.is_list/1 These functions and macros follow the Erlang convention of an is_ prefix, instead of a trailing question mark, precisely to indicate that they are allowed in guard clauses. Note that type checks that are not valid in guard clauses do not follow this convention. Examples: Keyword.keyword?/1, Regex.regex?/1

So, using one of the listed expressions that returns a boolean is ok for write compliant guard macros (if they haven't side effects).

Upvotes: 1

Onorio Catenacci
Onorio Catenacci

Reputation: 15343

There is a whole list of functions which can be used in guards here. If the function is not in that list, it cannot be used in a guard clause unless, as Dogbert points out above, you create a custom guard function which conforms to the rules he laid out.

EDIT:

See this page on which it says:

The set of valid guard expressions (sometimes called guard tests) is a subset of the set of valid Erlang expressions. The reason for restricting the set of valid expressions is that evaluation of a guard expression must be guaranteed to be free of side effects.

Emphasis in original text.

Hence any function which you use as a guard (built-in or a macro) must be guaranteed to be free of side-effects. Since that's hard to guarantee, the Erlang VM itself restricts which expressions can be used in a guard. If you write your own macro it's ultimately rendered as Elixir code anyway and if the Elixir code doesn't conform to the rules about guards, macro or not, it won't work.

I hope this makes things clearer.

Upvotes: 0

Dogbert
Dogbert

Reputation: 222388

All three of those functions contain expressions that are valid in guards; it's just that custom guards must be written as macros, not normal functions:

defmodule User do
  defstruct age: 0

  defmacro kid?(age) do
    quote do
      6 < unquote(age) and unquote(age) < 12
    end
  end

  defmacro teen?(age) do
    quote do
      12 < unquote(age) and unquote(age) < 18
    end
  end

  defmacro elder?(age) do
    quote do
      60 < unquote(age)
    end
  end
end

defmodule Greeting do
  import User

  def greet(%{age: age}) when kid?(age), do: "Hiya"
  def greet(%{age: age}) when teen?(age), do: "Whatever"
  def greet(%{age: age}) when elder?(age), do: "You kids get off my lawn"
  def greet(_), do: "Hello"
end

for age <- [0, 5, 10, 15, 20, 90] do
  IO.inspect {age, Greeting.greet(%{age: age})}
end

Output:

{0, "Hello"}
{5, "Hello"}
{10, "Hiya"}
{15, "Whatever"}
{20, "Hello"}
{90, "You kids get off my lawn"}

Upvotes: 5

Related Questions