elixirtruthinessguard-clause

Testing truthiness in guards


I can use guards to test if an argument is true:

defmodule Truth do
  def true?(term) when term, do: "#{term} is true"
  def true?(term), do: "#{term} is not true"
end

This works as expected for boolean values:

Truth.true?(true)
#=> "true is true"
Truth.true?(false)
#=> "false is not true"

But it cannot be tested for truthiness:

Truth.true?(1)
#=> "1 is not true"

Is it possible to test for truthiness in guards? For example, can the following function be written using guards in the style of true?/1 above?

def truthy?(term) do
  if term, do: "#{term} is truthy", else: "#{term} is falsey"
end

Solution

  • According to the official documentation for Guards:

    Guards start with the when keyword, which is followed by a boolean expression.

    So the expressions in guards must be boolean expressions.

    Truthiness in elixir is defined by macros such as if/2. These macros are not available inside guards, so we need another way to convert the terms to boolean values.

    We can see from the documentation (and implementation) of if/2 that the definition of truthiness is that false and nil are falsey, everything else is truthy. So we can use that to implement our truthiness guards:

    defmodule Truth do
      defguard is_falsey(term) when term in [false, nil]
      defguard is_truthy(term) when not is_falsey(term)
    
      def truthy?(foo) when is_truthy(foo), do: "#{foo} is truthy"
      def truthy?(foo), do: "#{foo} is falsey"
    end
    

    This then works as expected:

    Truth.truthy?(true)
    #=> "true is truthy"
    Truth.truthy?(1)
    #=> "1 is truthy"
    Truth.truthy?(false)
    #=> "false is falsey"
    Truth.truthy?(nil)
    #=> " is falsey"