gmile

gmile

Advice to avoid simple transitive compile-time dependency

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?

Marked As Solved

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).

Also Liked

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
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?

sodapopcan

sodapopcan

lol, ever on the eternal constants quest :smiley:

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

Where Next?

Popular in Questions Top

LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
quazar
How to set Jason to encode all fields in ecto schema, I don’t care about security and implementing only is taking long list of attributes...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
Fl4m3Ph03n1x
Background Let’s assume I have a typical GenServer that receives messages as requests, does some operation in a DB and returns responses....
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
sabri
Can someone explain the settings of pool_size of Ecto in config file? and what is the recommend size? Thanks
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

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
yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
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
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 27727 240
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 42633 214
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
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
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement