skwidbreth
skwidbreth

Reputation: 8404

Elixir macros - pattern match on an argument passed as a variable

I'm trying to get a better understanding of Elixir macros, but I'm having trouble figuring out how to set up a scenario where I can pattern match the argument that I pass to a macro when the value I'm trying to pass is a variable. Here's a simple example to illustrate:

macro_test.ex

defmodule MacroTest do
  use MacroTest.UseMe

  def run() do
    atom = :hello_world

    macro_a(atom)
  end
end

use_me.ex

defmodule MacroTest.UseMe do
  defmacro __using__(_) do
    quote do

      defmacro macro_a(:hello_world) do
        quote do
          "Success!"
        end
      end

      defmacro macro_a(:some_other_atom) do
        quote do
          "Did something else..."
        end
      end

    end
  end
end

When I try to compile this code, I get the error

== Compilation error in file lib/macro_test.ex ==
** (FunctionClauseError) no function clause matching in MacroTest.macro_a/1  

If I change the initial run() function so that the atom is passed directly to the macro, such as macro_a(:hello_world), then it compiles/runs just fine.

How can I alter this scenario so that the macro can pattern match on a value that has been provided as a variable, rather than the literal value itself?

Upvotes: 0

Views: 891

Answers (1)

legoscia
legoscia

Reputation: 41548

It depends on what you want to do. Macros run at compile time, and operate on portions of source code (as opposed to the run time value returned by said piece of source code). So in this case, the macro knows that it has been called with a variable called atom, but it has no way of knowing that this variable has been assigned to earlier in the calling function.

You could check the value of the given variable at run time:

  defmacro macro_a(a) do
    quote do
      case unquote(a) do
        :hello_world ->
          "Success!"
        :some_other_atom ->
          "Did something else..."
      end
    end
  end

That is, every invocation of macro_a gets replaced by the case expression above, using whatever variable or other expression was passed as argument.

Upvotes: 2

Related Questions