defmodule A do
defmacro __using__(_) do
IO.puts "A is being used"
end
end
defmodule B do
defmacro __using__(_) do
IO.puts "B is being used"
quote do: use A
end
end
defmodule C do
use A
use B
end
The console shows
A is being used
B is being used
A is being used
My question is, since module A has been explicitly used by C, how can I prevent A from being implicitly used again? Or more precisely, In module B, how can I not to use A whenever A has already been used?
defmodule A do
defmacro __using__(_) do
quote do
case Module.get_attribute(__MODULE__, :use_attempts) do
nil ->
Module.register_attribute __MODULE__, :use_attempts, accumulate: false, persist: false
IO.puts "A is being used for the first time"
Module.put_attribute(__MODULE__, :use_attempts, 1)
i when is_integer(i) ->
IO.puts "Prevented an attempt ##{i} to reuse the module"
Module.put_attribute(__MODULE__, :use_attempts, i + 1)
end
end
end
end
defmodule B do
defmacro __using__(_) do
IO.puts "B is being used"
quote do: use A
end
end
defmodule C do
use A
use B
end
Results in:
B is being used
A is being used for the first time
Prevented an attempt #1 to reuse the module
For note, a use Blah call is just a require Blah; Blah.__using__([]) call, so this is not asking like in a C way of embedding things, but rather you are asking how to prevent a function from being called twice, it doesn’t make much sense generally, but if you really want to then there are many ways to do so, like simple attributes as the above post shows.
If I had 2 modules that work like mixins, say module A and B, and a normal module C which needs functions provided by B. I can do this:
defmodule C do
use B
end
while B needs functions provided by A, but those functions need to be called in the context of C (because of some magical variables like __MODULE__). I can’t do this because A is not used by C.
defmodule B do
use A
defmacro __using__(_) do
quote do
def func_from_B do
func_from_A()
end
end
end
end
Of course I can use A in C, but why should I since func_from_A is not directly called in C?
I feel like this is a good time to point out that you should try to avoid using defmacro __using__ unless it is truly necessary. It is a rather unflexible construct.