mjlorenzo
Module Does Not Recompile When Referenced Module Changes
Hi folks, got a question about how Elixir determines when to recompile a module. The documentation says a module will recompile when one of its dependencies changes, but that doesn’t seem to be the case here and I’m not sure why. Here’s the scenario:
I have a module that defines a schema for the library I’m building:
defmodule Schema do
import Library.Schema.Notation
schema :name do
# macros and schema definition...
end
end
When this compiles, it exposes a callback __schema__ to be consumed in another module. While Library.Client actually consumes the schema, Testbed is the intended product module:
defmodule Testbed do
use Library.Client, schema: Schema
#. (Library.Client.__using__() actually calls Schema.__schema__())
# ... callbacks and implementation
end
I figured because Testbed references Schema, then it should know to recompile if Schema recompiles. I’m consistently seeing however that changes made to Schema do cause itself to recompile, but Testbed still doesn’t and I have to manually recompile it to reflect the changes in Schema. This ring a bell for anyone?
Thanks,
Mike
Most Liked
sodapopcan
With an oversimplified mix project, I am unable to reproduce this:
defmodule Recompile.Schema do
defmacro __schema__() do
quote do
"I do schema stuff!"
end
end
end
defmodule Recompile.Notation do
defmacro __using__([schema: schema]) do
quote do
require unquote(schema)
unquote(schema).__schema__()
end
end
end
defmodule Recompile.TestBed do
use Recompile.Notation, schema: Recompile.Schema
end
$ mix xref graph --label compile --sink lib/recompile/schema.ex
lib/recompile/test_bed.ex
└── lib/recompile/schema.ex (compile)
and when I change Schema:
$ mix compile --verbose
Compiling 2 files (.ex)
Compiled lib/recompile/schema.ex
Compiled lib/recompile/test_bed.ex
Could you possibly share more code and your Elixir version? Is this in a mix project?
sodapopcan
Yes, that is correct behaviour in this situation because it’s inside a function which makes it a runtime dependency.
sodapopcan
Mostly, yes. Nothing outside of the returned quoted expression will be injected into Testbed so once that module is compiled, all that stuff is gone. So in this case it’s not that it isn’t interpreting Schema as anything other than the atom it’s that Schema isn’t even present in the compiled code! Again it’s hard to see without more context but there doesn’t seem to be anything about that code snippet that suggests you need Macro.expand over requireing or importing within a quote block which of course you want anyway to create the dependency. It depends what you’re trying to accomplish, of course.
I’ve also never actually seen ensure_compiled! used inside a module before. I’m not a master of macros, though, so maybe there is a good reason, but AFAIK, ensure_compiled! is essentially require for use outside of a defmodule. Don’t quote me on that, though ![]()
sodapopcan
import yes, require no. require literally just ensures the module is compiled (namely that its macros have been expanded) before the module it’s called in, ie, required for the current module to work
It’s needed if there are macros in the dependency otherwise simply calling a function from it will suffice. For example:
defmodule Foo do
Bar.a_regular_ol_function_that_is_called_in_the_module_body_for_some_reason()
end
This will make Bar a dependency of Foo. It doesn’t need a require if Bar isn’t calling a macro.
(sorry for all the edits if you noticed that, my dog is bugging me to go outside but wanted to finish the response, lol)







