Why can't I run `Integer.is_even` in this guard clause?

Disregarding the poorly written code, I was wondering why I can’t run Integer.is_even inside the when guard

defmodule CollatzConjecture do
  @doc """
  calc/1 takes an integer and returns the number of steps required to get the
  number to 1 when following the rules:
    - if number is odd, multiply with 3 and add 1
    - if number is even, divide by 2
  """
  @spec calc(input :: pos_integer()) :: non_neg_integer()
  def calc(input) do
    one_step(input, 0)
  end

  defp one_step(input, step_count) do
    case input do
      1 -> step_count
      input when Integer.is_even(input) ->
        one_step(div(input,2), step_count + 1)
      _ -> 
        one_step((input * 3) + 1, step_count + 1)
    end
  end
end

ChatGPT claims it will compile and run - but it does not, and I get this error:

Compiling 1 file (.ex)
    error: cannot invoke remote function Integer.is_even/1 inside a guard
    │
 16 │       input when Integer.is_even(input) ->
    │                          ^
    │
    └─ lib/collatz_conjecture.ex:16:26: CollatzConjecture.one_step/2


== Compilation error in file lib/collatz_conjecture.ex ==
** (CompileError) lib/collatz_conjecture.ex: cannot compile module CollatzConjecture (errors have been logged)

I looked at the documentation for Integer.is_even (link) and it states that it is “allowed in guard clauses”. Am I misunderstanding what a guard clause is?

Untested but you probably need to add

require Integer

Integer.is_even/1 is a macro and you will need to require it first.

6 Likes

Ah thanks…I knew it was something silly like this.

1 Like

I’m sorry Dave, I’m afraid I can’t do that :robot:

def calc(1), do: 0
def calc(int) when int > 0 and rem(int, 2) == 1, do: 1 + calc(int * 3 + 1)
def calc(int) when int > 0 and rem(int, 2) == 0, do: 1 + calc(div(int, 2))
2 Likes

This was my next logical step, I promise! Thank you though :slight_smile:

The error message here is confusing here. I will improve it in main.

9 Likes