Why Elixir allows access to caller's module attributes but not variables

defmodule Mod do

  defmacro definfo do
    IO.puts("In macro's context (#{__MODULE__})")

    quote do
      IO.puts("In caller's #{@attr} context (#{__MODULE__})")

     def friendly_info do
      IO.puts """
      My name is #{__MODULE__}
      My functions are #{inspect __MODULE__.__info__(:functions)}
      """
     end
    end
  end
end


defmodule MyModule do
  @attr "Hello"
  require Mod


  Mod.definfo()
end

I’m currently reading Metaprogramming in Elixir, I understand the concept of macro hygiene, I was just wondering why elixir will allow access of a caller’s module attribute (in the above modified example, I can access the value of @attr) within the macro but frowns on accessing the caller’s variables unless explicitly indicated via var! since I presume both could be missing at compile time and yes I understand elixir will return nil if an attribute is missing. A bit of education will help.

2 Likes

Module attributes while accessible at runtime, are not changeable at runtime. This makes using them in macros quite a bit less surprising than trying to fiddle with (runtime) variables without proper signaling (passed as parameter / returned from macro).

5 Likes

To add to that, module attributes are not that common, so the odds of conflict are few and can be solved by namespacing module attributes with @my_lib_name. However, variables are everywhere, and therefore avoiding conflicts by default is more important.

5 Likes