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?

First 8 of 8 Posts Switch mode

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

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
bottlenecked
Hi all, I wanted to ask how the community is dealing with post-release steps. Today we have Ecto migrations, which make sure that the db...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
rahultumpala
Hello, I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance o...
New

Other Trending Topics Top

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
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New

We're in Beta

About us Mission Statement