Chedy2149
Chedy2149

Reputation: 3051

How to use bitwise operators in an elixir when guard clause?

I have the following function, that needs to match only with values of n being a power of 2 integers (1, 2, 4, 8, 16, 32, 64, ...):

defmodule MyModule do
  def my_func(n) when is_power_of_two(n) do
     ## Some expression
  end
  def is_power_of_two(x) do
    (x != 0)
        and (x &&& (x - 1)) == 0)
  end
end

First, I tried defining is_power_of_two as a function in the module but it didn't work and I got this error:

cannot invoke local is_power_of_two/1 inside guard.

Following this blog post, I tried to define it as the following macro:

defmodule MyModule.Util do
  defmacro is_power_of_two(x) do
    quote do
      (unquote(x) != 0)
        and ((unquote(x) &&& (unquote(x) - 1)) == 0)
    end
  end
end

Didn't work as well and I get the following error:

cannot invoke local &&&/2 inside guard

It seems that the bitwise &&& operator cannot be called from a when clause after the macro expansion.

How to perform matches that requires guards containing bitwise operators?

Upvotes: 1

Views: 562

Answers (1)

Dogbert
Dogbert

Reputation: 222158

The error message is misleading. You're just missing an import Bitwise in MyModule.Util. The code works if I add the import:

defmodule MyModule.Util do
  import Bitwise

  defmacro is_power_of_two(x) do
    quote do
      (unquote(x) != 0) and ((unquote(x) &&& (unquote(x) - 1)) == 0)
    end
  end
end

defmodule MyModule do
  import MyModule.Util

  def my_func(n) when is_power_of_two(n) do
    true
  end
  def my_func(_), do: false
end

IO.inspect MyModule.my_func(128)
IO.inspect MyModule.my_func(129)

Output:

true
false

Upvotes: 4

Related Questions