gmile
Strategy to avoid particular transitive compile-time dependency
As part of Understanding a spike in app compilation time, I’m trying to remove compile-time dependency that’s based on defguard macro.
With the code below, Elixir reports a transitive compile-time dependency:
mix xref graph --label compile-connected
lib/example1.ex
└── lib/guards.ex (compile)
How would one go about change the code to avoid the transitive dependency, while (ideally) preserving the defguard macros?
For context, this is a simplified example of production code. The reason lib/guards.ex is a separate file is code re-use - it’s typically included into other modules to offer the guards.
The code looks like this (repo is available here, for reference):
# lib/example1.ex
defmodule Example1 do
import Example1.Guards
def user(user) when is_admin(user), do: IO.puts("This user is an admin: #{inspect(user)}")
def user(user) when is_customer(user), do: IO.puts("This user is a customer: #{inspect(user)}")
end
# lib/guards.ex
defmodule Example1.Guards do
defguard is_admin(user) when is_struct(user, Example1.Admin) == "admin"
defguard is_customer(user) when is_struct(user, Example1.Customer) == "customer"
end
# lib/admin.ex
defmodule Example1.Admin do
defstruct role: :admin
end
# lib/customer.ex
defmodule Example1.Customer do
defstruct role: :customer
end
Since both defguard and is_struct(anyVariable, AnyModule) are not supposed to make any function calls on the AnyModule - maybe there’s no reason to report a compile-time dependency in such case, is there?
Marked As Solved
josevalim
You can print the previous comment in case it has any persuasive power ![]()
BUT GOOD NEWS
It will no longer be required from Elixir v1.15. I realized that patterns and guards cannot add runtime dependencies to modules because they can never call said modules, so the compile-connected dependency should fully disappear in v1.15.
Thanks everyone for the helpful discussion!
Also Liked
LostKobrakai
@admin Module.concat(["Example1", "Admin"])
@customer Module.concat(["Example1", "Customer"])
defguard is_admin(user) when is_struct(user, @admin)
defguard is_customer(user) when is_struct(user, @customer)
This seems to work. Module.concat is also what you’d commonly see in macros for those issues.
josevalim
The only reason you are doing it is to work around a compiler behaviour and that to me is a smell. ![]()







