gmile

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?

Showing Posts 1 to 8

LostKobrakai

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_way due to limitations of guards. But it could also be a normal macro, which might call by_the_way at some point during execution.

hauleth

hauleth

Because compiler do not know (and in general sense it cannot know) that A do not depend on B.by_the_way. Phoenix generates module that is perfect example of that:

def module MyAppWeb do
  defmacro __using__(name) do
    apply(__MODULE__, name, [])
  end

  # …
end

This shows that calling macro can call any function within our module.

It can:

defmodule Foo do
  defguard is_foo(a) when foo(a)

  defmacro foo(a) do
    by_the_way()

    quote do: true
  end

  def by_the_way, do: C.something()
end
LostKobrakai

LostKobrakai

Good point. A macro could be part of the defguard and that macro can then call anything again.

defmodule Foo do
  defguard is_foo(a) when foo()

  defmacro foo() do
    if by_the_way() do
      quote do: true
    else
      quote do: false
    end
  end

  def by_the_way, do: C.something()
end
josevalim

josevalim

Creator of Elixir

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

dimitarvp

Well, at least there is one thing that I do that is recommended by the core team. :003:

JEG2

JEG2

Author of Designing Elixir Systems with OTP

Are “constants” in this context just functions that return a literal value?

josevalim

josevalim

Creator of Elixir

Yes! Values that you would want to access in guards and patterns.

sodapopcan

sodapopcan

lol, ever on the eternal constants quest :smiley:

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews