@@ -107,6 +107,115 @@ iex> MyMath.sum(3+1, 5+6)
107
107
15
108
108
```
109
109
110
+ ## Transitive dependencies
111
+
112
+ #### Context
113
+
114
+ This anti-pattern is related to dependencies between files in Elixir. Elixir tracks
115
+ three different types of dependencies between compiled files to know when a file needs
116
+ to be recompiled. The different dependency types are explained in the documentation of
117
+ [ ` mix xref ` ] ( https://hexdocs.pm/mix/Mix.Tasks.Xref.html#module-dependency-types ) .
118
+
119
+ #### Problem
120
+
121
+ Macro-exposing modules are bound to be compile-time dependencies of the modules using
122
+ their macros. If they depend on other modules from within the same project, then they
123
+ become the source of compile-connected dependencies. This means that if the file
124
+ containing the module they depend on is modified, all files depending on this module will
125
+ need to be recompiled.
126
+
127
+ This becomes especially problematic when projects grow and the number of compile-connected
128
+ dependencies is not kept under control, making modifying any file trigger a recompilation
129
+ of many other files and slowing down the development process.
130
+
131
+ Note that depending on modules from external libraries is not a problem, as these modules
132
+ are only compiled once when the library is compiled.
133
+
134
+ #### Example
135
+
136
+ The code below defines three modules ` ModuleA ` , ` Macros ` and ` MacroUtils ` , each in their
137
+ own file. The dependencies between the three files are as follows:
138
+
139
+ - ` lib/module_a.ex ` has a _ compile_ dependency on ` lib/macros.ex ` because ` ModuleA `
140
+ calls a macro defined in ` Macros ` ,
141
+ - ` lib/macros.ex ` has a _ runtime_ dependency on ` lib/macro_utils.ex ` because ` Macros `
142
+ calls a function defined in ` MacroUtils ` within the body of its macro.
143
+
144
+ In this example, ` lib/macro_utils.ex ` is a compile dependency of ` lib/module_a.ex ` ,
145
+ through a transitive dependency via ` lib/macros.ex ` . This means that modifying
146
+ ` lib/macro_utils.ex ` will trigger a recompilation of ` lib/module_a.ex ` , as if
147
+ ` lib/module_a.ex ` had a direct compile dependency on ` lib/macro_utils.ex ` .
148
+
149
+ Note that if ` MacroUtils ` itself depended on other modules directly or indirectly,
150
+ modifying the files containing these modules would also trigger a recompilation of
151
+ ` lib/module_a.ex ` .
152
+
153
+ ``` elixir
154
+ # lib/module_a.ex
155
+ defmodule ModuleA do
156
+ require Macros
157
+
158
+ def hello do
159
+ Macros .greet ()
160
+ end
161
+ end
162
+ ```
163
+
164
+ ``` elixir
165
+ # lib/macros.ex
166
+ defmodule Macros do
167
+ defmacro greet do
168
+ module = MacroUtils .get_module (__CALLER__ )
169
+
170
+ quote do
171
+ IO .puts (" Hello from #{ unquote (module)} " )
172
+ end
173
+ end
174
+ end
175
+ ```
176
+
177
+ ``` elixir
178
+ # lib/macro_utils.ex
179
+ defmodule MacroUtils do
180
+ def get_module (caller) do
181
+ caller.module
182
+ end
183
+ end
184
+ ```
185
+
186
+ #### Refactoring
187
+
188
+ To remove this pattern, the developer must remove any dependency to other modules from
189
+ the same project. In the code shown below, the ` MacroUtils ` module was deleted and the
190
+ ` get_module/1 ` function was moved to the ` Macros ` module. This way, the ` Macros ` module
191
+ no longer depends on any other module.
192
+
193
+ ``` elixir
194
+ # lib/macros.ex
195
+ defmodule Macros do
196
+ defmacro greet do
197
+ module = get_module (__CALLER__ )
198
+
199
+ quote do
200
+ IO .puts (" Hello from #{ unquote (module)} " )
201
+ end
202
+ end
203
+
204
+ defp get_module (caller) do
205
+ caller.module
206
+ end
207
+ end
208
+ ```
209
+
210
+ #### Additional remarks
211
+
212
+ Whilst this guide only covers macros, modules can be compile dependencies of other modules
213
+ in other ways. For example, calling another module in the module, in a module attribute
214
+ for example, will also create a compile dependency. Some external libraries may also
215
+ create compile dependencies by using macros.
216
+ [ ` mix xref ` ] ( https://hexdocs.pm/mix/main/Mix.Tasks.Xref.html#module-a-brief-introduction-to-xref )
217
+ can be useful to identify these dependencies and refactor the code to remove them.
218
+
110
219
## ` use ` instead of ` import `
111
220
112
221
#### Problem
0 commit comments