Reputation: 301
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
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
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.
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
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