lurodrigo

lurodrigo

Help understanding compile-time dependencies when referencing aliases in module attributes

I’m refactoring some parts of a codebase that do some compile-time magic, in order to reduce the number of compile-time dependencies between modules. One thing I don’t fully get is when nominal references to other modules/aliases in module attributes create a compile-time dependency.

I have a minimal example here (compiling with elixir 1.15.7-otp-26)

If I have these two modules:

defmodule Trace.Submodules.Submodule do
end

and

defmodule Trace do
  alias Trace.Submodules

  @attribute Submodules.Submodule
  @attribute Submodules.DoesNotExist
  @attribute [Submodules.Submodule]
  @attribute [Submodules.DoesNotExist]
  @attribute to_string(Submodules.Submodule)
  @attribute to_string(Submodules.DoesNotExist)
  @attribute to_string(Submodules.Submodule) <> to_string(Submodules.DoesNotExist)
  @attribute (& &1).(Submodules.Submodule)

  def attribute, do: @attribute
end

When I run mix xref trace lib/trace.ex --label compile it shows Trace three places where there are compile-time dependencies on Trace.Submodules.Submodule → precisely where there are function calls wrapping Subodules.Submodule, but not a direct reference or just wrapping in a list.

lib/trace.ex:8: alias Trace.Submodules.Submodule (compile)
lib/trace.ex:10: alias Trace.Submodules.Submodule (compile)
lib/trace.ex:11: alias Trace.Submodules.Submodule (compile)

It’s weirder because I do the exact same operations for Submodules.DoesNotExist, which doesn’t exist, and there’s no problem. Is it the case the compiler assumes the alias can be used as a module by the function at compile-time, so it places a compile-time dependency on it?

Asking bc the reason behing the behaviour impacts how I’ll approach refactoring. Any help appreciated!

Marked As Solved

xpg

xpg

That explanation makes sense to me. Good analysis!

Applying your way of thinking (and a bit looking and xref source code) also turned out to explain and solve my situation: Macro.expand_literals/2 will indeed add compile-time dependencies if the provided environment (__CALLER__ in my case) represents the body of a module. But it will only add a runtime dependency, if the environment represents the body of a function.
The documentation of Macro.expand_literals/2 hints that you can just modify the environment passed in, such that the code “thinks” you are in a function body. Unfortunately, I didn’t quite understand the hint for quite some time :slight_smile:

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New
wernerlaude
In AR this is so simple @articles = current_user.articles How to do in Ecto? def index(conn, _params) do current_user = conn.assig...
New
lucidguppy
I have a super simple question about elixir - how would I take a file like this foo bar baz and output a new file that enumerates th...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod -- where is this set? Thanks.
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
beno
I will often find my self writing things similar to: case some_value do nil -&gt; something() "" -&gt; something() _ -&gt; someth...
New

We're in Beta

About us Mission Statement