Determining which module invoked a function( At Runtime)?

I have a module utils,which is supposed to be used by MOdule A, B,C,D… and so on. The issue is utils modules have lot of constants, constants specific to module A,B,C,D … . When i call module utils functions from module A, I want those functions to use constants defined in module A. same for B,C,D,… .What to use Import, use,etc.

So I can call module utils functions, so those util module functions can load constants depending upon which module has made the call.Opposed to passing as variable to the utils module functions. (since there is lot of functions).

So how to determine which module has invoked a function in another module at runtime. So I can leverage that information.

There is discussion about this here.

I can’t quite visualize what you’re trying to do without seeing code, but if you have code like this:

defmodule A do
  @module B
end

I would advice against that since it results in B being a compile time dependency of A. If you have a lot of stuff like this, compilation can start to get really slow if you aren’t careful about your dependency graph. Otherwise—carry on.

defmodule Z do
@cons some_val 
#to be determine by caller module
    def func() do
       do_something_to(@cons)
     end
end
defmodule A do
@cons some_val
# or define it by some other means
    def do() do
      Z.func()
     end
end
defmodule B do
@cons some_val
    def do() do
      Z.func()
     end
end
defmodule C do
@cons some_val
    def do() do
      Z.func()
     end
end

It can be resolved by passing this extra variable from module, but issue is the function which use this constants are deeply nested, will require to add parameters to all already existing funcs

I can’t quite picture the nesting but it seems like you might be trying to re-create inheritance here?? There are likely better ways to do what you want to do though not sure how to advise. Hopefully you figured it out via that other topic!

It’s possible to do something similar with macros, but fair warning the general rule for macros is not to use them.

defmodule Util do
  defmacro do_something() do
    quote do
      Util.do_something_with(@constant)
    end
  end

  def do_something_with(constant) do
    IO.puts("The constant is #{constant}")
  end
end

defmodule A do
  require Util
  @constant 1

  def test() do
    Util.do_something()
  end
end

defmodule B do
  require Util
  @constant 2

  def test() do
    Util.do_something()
  end
end

defmodule C do
  require Util
  @constant 3

  def test() do
    Util.do_something()
  end
end

A.test()
# The constant is 1

B.test()
# The constant is 2

C.test()
# The constant is 3

2 Likes

I’m not going to even speculate along this line, because you shouldn’t do this.

Let’s talk about the underlying requirement instead.

Can you be more specific about what you mean by “having constants”? Modules don’t really “contain” anything - for instance, there’s no function in Module to get [Foo.Bar, Foo.Baz, Foo.Wat] given just Foo.

A common pattern for this is two parts:

  • functions that accept the module as an additional argument beyond the “public” API
  • a use macro that creates functions that include this extra argument in a user-controlled namespace

An example of this is Ecto.Repo.insert:

The underlying function is Ecto.Repo.Schema.insert, which accepts the repo as a first argument. In a default configuration with nobody calling put_dynamic_repo, that argument will be the module that said use Ecto.Repo.

In your case, you’d have a module in Utils that defines the __using__ macro and then use it in each of A, B, C, D like:

defmodule A.Utils do
  use Utils, some_config: "specific to A"
end

# in other A code:
A.Utils.do_stuff("x", "y")
# which then calls
Utils.do_stuff(A, "x", "y")
1 Like