TIL

You can override existing infix operators in your modules by very simple means. When ever I needed to implement and/or functions in my modules, I’ve defaulted to using and_/or_ function names, since a module with and/or functions defined as below fails with a syntax error:

defmodule Operator do
  def and(left, right) do
    :ok
  end
end

So I’ve been doing this:

defmodule Operator do
  def and_(left, right) do
    :ok
  end

  def or_(left, right) do
    :ok
  end
end

I always considered it to be due to and being a keyword and not being able use the same name in my functions. This is however not the case. It just have to use the correct def construct:

defmodule Operator do
  # with pattern matching
  def (%{key: value} = _left) and (%{key: value} = _right) do
    :ok
  end

  def left and right do
    :ok
  end
end

However what happens when having another function which uses and in guards?

defmodule Operator do
  def left and right do
    :ok
  end

  def other_function(value1, value2) when value1 == 1 and value2 == 2 do
    :ok
  end
end
$ mix compile
** (CompileError) : imported Kernel.and/2 conflicts with local function
    (elixir 1.12.2) src/elixir_locals.erl:94: :elixir_locals."-ensure_no_import_conflict/3-lc$^0/1-0-"/2
    (elixir 1.12.2) src/elixir_locals.erl:95: anonymous fn/3 in :elixir_locals.ensure_no_import_conflict/3
    (stdlib 3.15.2) erl_eval.erl:685: :erl_eval.do_apply/6

Kernel functions are automatically imported, Kernel.and/2 is a built in macro that can be used in guard expressions. As long as were not using Kernel.and/2 in guards we can call it directly from the function to avoid the conflict i.e:

def some_func(value1, value2) do
  Kernel.and(value1 == 1, value2 == 2)
end

And actually the same thing can be done to resolve the conflict for the guards as well:

defmodule Operator do
  alias Kernel, as: K

  def left and right do
    :ok
  end

  def other_function(value1, value2) when K.and(value1 == 1, value2 == 2) do
    :ok
  end
end