gmile
I am trying to understand why transitive compile-time dependency is there, and what’s a good strategy to remove it. The mix app for example below is available here.
In a nutshell, I have these 3 modules:
# a.ex
defmodule A do
import B, only: [valid_string: 1]
def check(string) when valid_string(string), do: "OK"
end
# b.ex
defmodule B do
defguard valid_string(string) when string in ~w(read write)
def by_the_way do
C.something()
end
end
# c.ex
defmodule C do
def something do
IO.puts("Just passing by")
end
end
Dependencies from this module form a what’s known as transitive compile-time dependency. This can be confirmed by running mix href command:
mix xref graph --label compile-connected
Output:
lib/a.ex
└── lib/b.ex (compile)
If I remove by_the_way function from module B, mix xref no longer reports a transitive compile-time dependency. Could someone explain to me why A transitively depends on C, even though A is only interested in importing the macro from B, and doesn’t really care about the function by_the_way or its implementation?
What’s a good strategy to remove such transitive compile time dependency? Is it like separating macros and functions in different modules?
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex











First 8 of 8 Posts
LostKobrakai
That’s a quite a few assumptions about the knowledge of the compiler. In this specific case the imported functionality is a guard, which indeed cannot depend on
by_the_waydue to limitations of guards. But it could also be a normal macro, which might callby_the_wayat some point during execution.hauleth
Because compiler do not know (and in general sense it cannot know) that
Ado not depend onB.by_the_way. Phoenix generates module that is perfect example of that:This shows that calling macro can call any function within our module.
It can:
LostKobrakai
Good point. A macro could be part of the defguard and that macro can then call anything again.
josevalim
Elixir tracks dependencies at the module level. One common approach to address this is to define constants (which you may access at compile-time, such as
invoice_states) and guards that are used across multiple modules into a separate module that only defines constants/guards (and does not call anything else).dimitarvp
Well, at least there is one thing that I do that is recommended by the core team.
JEG2
Are “constants” in this context just functions that return a literal value?
josevalim
Yes! Values that you would want to access in guards and patterns.
sodapopcan
lol, ever on the eternal constants quest