Dynamic require error

Hello,

I’m doing a library where I want to require some module dynamically to call some macros (which will generate dynamic functions to the current module). The code could be something like the following snippet:

defmodule A do
  defmacro foo() do
  end
end

defmodule B do
  defmacro foo() do
  end
end

defmodule MetaMacro do
  defmacro __using__(modules) do
    quote bind_quoted: [modules: modules] do
      Enum.each(modules, fn module ->
        require module
        module.foo()
      end)
    end
  end
end

defmodule Bar do
  use MetaMacro, [A, B]
end

And it generates the following error:

invalid argument for require, expected a compile time atom or alias, got: module

I tried different solutions, but nothing works as I expected. Any help? Opinions?

Thank you for your time!

I just discovered this myself when looking at your issue. Elixir doesn’t allow you to call require with a variable. You can see that with this iex session.

iex(51)> defmodule Foo do
...(51)> end
iex(52)> foo = Foo
Foo
iex(53)> require foo
** (CompileError) iex:53: invalid argument for require, expected a compile time atom or alias, got: foo
iex(53)> require Foo
Foo

The below works for me:

defmodule A do
  defmacro foo() do
    IO.inspect(__MODULE__, label: inspect(__CALLER__.module))
  end
end

defmodule B do
  defmacro foo() do
    IO.inspect(__MODULE__, label: inspect(__CALLER__.module))
  end
end

defmodule MetaMacro do
  defmacro __using__(modules) do
    Enum.map(modules, fn module ->
      quote do
        require unquote(module)
        unquote(module).foo()
      end
    end)
  end
end

defmodule Bar do
  use MetaMacro, [A, B]
end
1 Like

BTW, I use this to debug AST’s, which may be useful for you. I find it helpful to see the code I generate and then execute that manually myself.

ast |> Macro.expand(__ENV__) |> Macro.to_string() |> Code.format_string!() |> IO.puts()

For example, I used it here:

defmodule MetaMacro do
  defmacro __using__(modules) do
    result = Enum.map(modules, fn module ->
      quote do
        require unquote(module)
        unquote(module).foo()
      end
    end)
    result |> Macro.expand(__ENV__) |> Macro.to_string() |> Code.format_string!() |> IO.puts()
    result
  end
end
2 Likes

Thank you! Good to know!