nathanl
I’m confronted with a case where mix xref graph --label compile-connected --fail-above 0 would force me to refactor something which seems unnecessary. I’ll give a simplified example, starting with an expected case and then showing my case.
Expected case
Here is a clear transitive compile-time dependency, where A depends on B which depends on C.
(Note: pretend that each of these modules is in its own file.)
- If
C.hello/0changes, that meansBneeds to recompile andB.hello/0changes - If
B.hello/0changes, that meansAneeds to recompile andA.hello/0changes
defmodule A do
@hello B.hello()
def hello, do: @hello
end
defmodule B do
@hello C.hello()
def hello, do: @hello
end
defmodule C do
def hello, do: "hello"
end
We can find this out like this:
$ mix xref graph --label compile-connected --fail-above 0
lib/a.ex
└── lib/b.ex (compile)
** (Mix) Too many references (found: 1, permitted: 0)
If we want to know “why is it bad that A depends on B? What does B depend on?”, we can check:
$ mix xref graph --source lib/b.ex
lib/b.ex
└── lib/c.ex (compile)
(Tangential point: it would be nice if the first command would show this automatically; its output implies “A shouldn’t depend on B” when maybe the truth is that B shouldn’t depend on C.)
A More Confusing Case
Here is a more confusing case, like the one I’m facing:
- If
C.world/0changes, that meansBneeds to recompile andB.world/0changes - If
B.world/0changes, I’m pretty sure that A isn’t going to compile any differently, but because theAdepends at compile time onBforB.hello/0, it will get (needlessly?) recompiled.
defmodule A do
@hello B.hello()
def hello, do: @hello
end
defmodule B do
@hello "hello"
def hello, do: @hello
def world, do: C.world()
end
defmodule C do
def world, do: "world"
end
The mix xref commands above see these two situations as the same, even though to me they seem quite different. This seems like a shortcoming of mix xref and/or the compile process.
Am I wrong?
Trending in Discussions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Most Liked- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
nathanl
Further conversation in Elixir Slack with Michał Muskała:
al2o3cr
My interpretation of this situation is that you’re tracking more detail in your mental model of the compiler than the compiler actually uses.
“
C.world/0changes” is just “Cchanges” to the compiler.Last Post!
fmn
thanks for sharing! i am just naively wondering - shouldn’t compiler know/navigate/traverse that graph, at least to some extent anyway?