How do I make a function globally available (within an app) without importing it everywhere?

Let’s say I have this function:


  def blank?(str_or_nil),
    do: "" == str_or_nil |> to_string() |> String.trim()

Let’s say this is a function in a module that’s something like GlobalUtils.

Is it possible to import this module at the application scope so that all modules have access to this function by default?

Just fully qualify everywhere.

1 Like

Not automatically…

However, I was inspired by the Phoenix generated my_app_web.ex file and how it dynamically “uses” macros which inject imports, uses, aliases, and requires given the module type.

So if you add to your my_app.ex file code like this:

defmodule MyApp do
  def context do
    quote do
      import GlobalUtils
      require Logger
    end
  end

  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end

  defmacro __using__({which, opts}) when is_atom(which) do
    apply(__MODULE__, which, [opts])
  end
end

Now at the top of each of your context modules just use MyApp, :context.

To this file you can add different functions for each module type you have (say… “controller”, “schema”, “resolver”, etc) to customize each module type’s imports, uses, aliases, and requires.

1 Like

Though before pulling/injecting arbitrary functions in the current namespace, one should be aware of the issues it causes.

Personally I prefer to read a thousand remote calls, fully qualified (or aliased), over a single foo imported from elsewhere, even worse if we have plenty of imports or the import is hidden behind some use-call…

4 Likes

In the example I gave I’m pretty sure we are not “injecting” functions in the current namespace since the macro just calls import and require and doesn’t define any new functions.

On your point yes I agree with you this pattern makes the code less explicit and can be overused especially if importing a lot of modules/functions hidden behind use calls. I tend to use this to lessen boilerplate of aliases, requires, module attributes for Ecto schemas, etc that I need in all modules of a given type and keep use of import to a bare minimum.

Yes, it does not define any new functions, but still it forbids you to implement functions that get imported… If you import Foo and there is a function bar/0 in that module, you can not implement that function in the importing module.

But even if you never want to do that, once you encounter bar() in your code, you search the current file for its definition, but don’t find it. Okay, lets look at the iomports! Well… There are none… Okay, which of these 3 use does do any import? And which modules are imported? Its just annoying to debug, once went through that…

5 Likes