Why require Logger when I'm already prefixing the func/macro name with the module name?

This is the code,

Logger.error("File.error: #{reason}")
[]

And this is the warning generated,

warning: you must require Logger before invoking the macro Logger.error/1

If I add require Logger inside the module or at the top of the file, the warning goes away.

Why do we need to require Logger if the function macro name is already prefixed with the module name?

Thank you!

Because it is a macro. Macros aren’t available by default, you must opt-in to use them by using either require or import. Quoting the require docs:

Public functions in modules are globally available, but in order to use macros, you need to opt-in by requiring the module they are defined in.

6 Likes

Macros can totally change the code that you see. If they were not opt-in, you could monkey patch the whole codebase like in Ruby or do JavaScript-like prototype fiddling. While this is still possible, it’s limited to a specific module and has to be explicitly requested by using use (“please inject some code here”), require (“I’m going to use macros from this module”) or import (make all/selected functions and macros available).

4 Likes

Thank you @Nicd and @stefanchrobot!

If I don’t require, the code still works, but it gives a warning.

Can you show the whole code? I get a compile error if I don’t require Logger.

1 Like

That is not exactly true. The main reason why you need require or import (use is just wrapper over require) is that you need to compile module with macro definition to happen before your module will be compiled, otherwise the compiler do not know what macro should expand to. The reason why it cannot be done automatically is that there is no way to know whether given construct is a macro or just regular function without first compiling the another module. Theoretically it shouldn’t be required for Logger, as this module is almost always already compiled when you compile your project, but for sake of the consistency it is better to have it there.

4 Likes

Following is the code (from Elixir for Programmers course by Dave Thomas):

defmodule Example do

  def read_file({:ok, file}) do
    file
    |> IO.read(:line)
  end

  def read_file({:error, reason}) do
    Logger.error("File.error: #{reason}")
    []
  end
end

Then use it like,

iex(1)> ".gitignore" |> File.open |> Example.read_file
"# The directory Mix will write compiled artifacts to.\n"

Suppose you want to use Logger all over your application (which I think makes sense but not sure how others do it). Does this mean you have to require Logger at the top of each file? Is there some way to modify the AppWeb config so that Logger is always included and I don’t have to worry about this anymore?

Perhaps you can add it in the macros in myapp_web.ex?

I tried adding it into “view_helpers” macro but it didn’t work. I also tried adding it into the app_web.ex file alone but that didn’t work either. Frankly, I’m not well versed on macro development or how this part of the infrastructure works, so probably missing something, but i you have thoughts as to how to set this up or best practices advice for what you normally do, would be appreciated. For now, I’ll just require logger everywhere…