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.

5 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.

3 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"