How to load a module before another loads in the same file in Elixir? Ask Question

I’m trying to implement some features of Protobuf and I have a case: A module needs to call another module in compiling time. They are in the same file and it’s hard to ensure their order.

defmodule FakeProtobuf do
  defmacro __using__(opts) do
    quote do
      # register_attribute fields and set fields
      @before_compile FakeProtobuf
    end
  end

  defmacro __before_compile__(_) do
    # result = Enum.map(fields, fn ...)

    # even Code.ensure_loaded doesn't work
    result = %{bar: Bar.default}
    quote do
      def default do
        unquote(Macro.escape(result))
      end
    end
  end
end

defmodule Foo do
  use FakeProtobuf
  # field :bar, type: Bar
end

defmodule Bar do
  def default do
    "bar"
  end
end

Some macro details related to fields are ignored in this piece of code, but the main idea is like what I said above. This code can’t be compiled because Bar is not available when Foo is compiled even when Code.ensure_loaded(Bar) is called. I need this because I want to run some code in compiling time instead of runtime to save some time.

It works if Bar is defined before Foo or in another file. But it’s hard to ensure this in protobuf generated files.

Is there anyway to solve this?

You would need to require module B into module A. require will precompile module B so A can use it.

1 Like

I don’t get it. require just let you use macros of that Module. The problem I describe is Bar is not compiled when Foo is compiled.

Code like this can’t work

defmodule Foo do
  require Bar
end

defmodule Bar do
end

This is not allowed. You can’t use macros from the same compilation unit.

4 Likes

From my understanding:

Macros are expanded at compile time.

require pre compiles the module and it so happens it converts the macros into usable code. If you have the modules in separate files, I believe it should work.