Skip to content

Commit 3b77086

Browse files
authored
Define "nutrition facts" for "use SomeModule" (#12613)
One common concern about using Elixir libraries is that "use SomeModule" does not make clear how it impacts the caller. Most of the time those changes are minor but it is impossible to know without traversing the source code. This pull request suggests defining a summary alongside each module that succintly explains the impact of using it. This is documented as best practice in our guides and we added the note to all of Elixir modules.
1 parent ea185b8 commit 3b77086

File tree

12 files changed

+132
-25
lines changed

12 files changed

+132
-25
lines changed

lib/elixir/lib/agent.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ defmodule Agent do
4949
Thanks to the agent server process, the counter can be safely incremented
5050
concurrently.
5151
52+
> #### `use Agent` {: .info}
53+
>
54+
> When you `use Agent`, the `Agent` module will define a
55+
> `child_spec/1` function, so your module can be used
56+
> as a child in a supervision tree.
57+
5258
Agents provide a segregation between the client and server APIs (similar to
5359
`GenServer`s). In particular, the functions passed as arguments to the calls to
5460
`Agent` functions are invoked inside the agent (the server). This distinction

lib/elixir/lib/application.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,13 @@ defmodule Application do
165165
end
166166
end
167167
168+
> #### `use Application` {: .info}
169+
>
170+
> When you `use Application`, the `Application` module will
171+
> set `@behaviour Application` and define an overridable
172+
> definition for the `c:stop/1` function, which is required
173+
> by Erlang/OTP.
174+
168175
The `c:start/2` callback has to spawn and link a supervisor and return `{:ok,
169176
pid}` or `{:ok, pid, state}`, where `pid` is the PID of the supervisor, and
170177
`state` is an optional application state. `args` is the second element of the

lib/elixir/lib/dynamic_supervisor.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ defmodule DynamicSupervisor do
123123
`use DynamicSupervisor` will be attached to the generated `child_spec/1`
124124
function.
125125
126+
> #### `use DynamicSupervisor` {: .info}
127+
>
128+
> When you `use DynamicSupervisor`, the `DynamicSupervisor` module will
129+
> set `@behaviour DynamicSupervisor` and define a `child_spec/1`
130+
> function, so your module can be used as a child in a supervision tree.
131+
126132
## Name registration
127133
128134
A supervisor is bound to the same name registration rules as a `GenServer`.

lib/elixir/lib/gen_server.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ defmodule GenServer do
8181
A `cast/2` message must be handled by `c:handle_cast/2`. `GenServer`
8282
supports 8 callbacks, but only `c:init/1` is required.
8383
84+
> #### `use GenServer` {: .info}
85+
>
86+
> When you `use GenServer`, the `GenServer` module will
87+
> set `@behaviour GenServer` and define a `child_spec/1`
88+
> function, so your module can be used as a child
89+
> in a supervision tree.
90+
8491
## Client / Server APIs
8592
8693
Although in the example above we have used `GenServer.start_link/3` and

lib/elixir/lib/kernel.ex

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5805,13 +5805,41 @@ defmodule Kernel do
58055805
58065806
In such cases, developers should instead import or alias the module
58075807
directly, so that they can customize those as they wish,
5808-
without the indirection behind `use/2`.
5808+
without the indirection behind `use/2`. Developers must also avoid
5809+
defining functions inside `__using__/1`.
58095810
5810-
Finally, developers should also avoid defining functions inside
5811-
the `__using__/1` callback, unless those functions are the default
5812-
implementation of a previously defined `@callback` or are functions
5813-
meant to be overridden (see `defoverridable/1`). Even in these cases,
5814-
defining functions should be seen as a "last resort".
5811+
Given `use MyModule` can generate any code, it may not be easy for
5812+
developers to understand the impact of `use MyModule`.
5813+
5814+
For this reason, to provide guidance and clarity, we recommend developers
5815+
to include an admonition block in their `@moduledoc` that explains how
5816+
`use MyModule` impacts their code. As an example, the `GenServer` documentation
5817+
outlines:
5818+
5819+
> #### `use GenServer` {: .info}
5820+
>
5821+
> When you `use GenServer`, the `GenServer` module will
5822+
> set `@behaviour GenServer` and define a `child_spec/1`
5823+
> function, so your module can be used as a child
5824+
> in a supervision tree.
5825+
5826+
This provides a quick summary of how using a module impacts the user code.
5827+
Keep in mind to only list changes made to the public API of the module.
5828+
For example, if `use MyModule` sets an internal attribute called
5829+
`@_my_module_info` and this attribute is never meant to be public,
5830+
it must not be listed.
5831+
5832+
For convenience, the markup notation to generate the admonition block
5833+
above is:
5834+
5835+
```
5836+
> #### `use GenServer` {: .info}
5837+
>
5838+
> When you `use GenServer`, the GenServer module will
5839+
> set `@behaviour GenServer` and define a `child_spec/1`
5840+
> function, so your module can be used as a child
5841+
> in a supervision tree.
5842+
```
58155843
"""
58165844
defmacro use(module, opts \\ []) do
58175845
calls =

lib/elixir/lib/supervisor.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,13 @@ defmodule Supervisor do
397397
`c:init/1` callback. `Supervisor.init/2` accepts the same `:strategy`,
398398
`:max_restarts`, and `:max_seconds` options as `start_link/2`.
399399
400+
> #### `use Supervisor` {: .info}
401+
>
402+
> When you `use Supervisor`, the `Supervisor` module will
403+
> set `@behaviour Supervisor` and define a `child_spec/1`
404+
> function, so your module can be used as a child
405+
> in a supervision tree.
406+
400407
`use Supervisor` also defines a `child_spec/1` function which allows
401408
us to run `MyApp.Supervisor` as a child of another supervisor or
402409
at the top of your supervision tree as:

lib/elixir/lib/task.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ defmodule Task do
164164
and `Task.start_link/1` are for fire-and-forget tasks, where you don't
165165
care about the results or if it completes successfully or not.
166166
167+
> #### `use Task` {: .info}
168+
>
169+
> When you `use Task`, the `Task` module will define a
170+
> `child_spec/1` function, so your module can be used
171+
> as a child in a supervision tree.
172+
167173
`use Task` defines a `child_spec/1` function, allowing the
168174
defined module to be put under a supervision tree. The generated
169175
`child_spec/1` can be customized with the following options:

lib/elixir/pages/library-guidelines.md

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ you should prefer:
6565
```elixir
6666
case File.read("some_path_that_may_or_may_not_exist") do
6767
{:ok, contents} -> {:it_worked, contents}
68-
 {:error, _} -> :it_failed
68+
{:error, _} -> :it_failed
6969
end
7070
```
7171

@@ -200,7 +200,31 @@ The code above says we are only bringing in the functions from `MyLib` so we can
200200

201201
If the module you want to invoke a function on has a long name, such as `SomeLibrary.Namespace.MyLib`, and you find it verbose, you can leverage the `alias/2` special form and still refer to the module as `MyLib`.
202202

203-
While there are situations where `use SomeModule` is necessary, `use` should be skipped when all it does is to `import` or `alias` other modules. In a nutshell, `alias` should be preferred, as it is simpler and clearer than `import`, while `import` is simpler and clearer than `use`.
203+
### Avoid undocumented `use SomeModule`
204+
205+
In some situations, where you need to do more than importing and aliasing modules, allowing a developer to `use SomeModule` may be necessary. The benefit of `use SomeModule` is that it provides a common extension point for the Elixir ecosystem. However, given `use SomeModule` can execute any code, it may not be easy for developers to understand the impact of `use SomeModule`.
206+
207+
For this reason, to provide guidance and clarity, we recommend library authors to include an admonition block in their `@moduledoc` that explains how `use SomeModule` impacts the developer's code. As an example, the `GenServer` documentation outlines:
208+
209+
> #### `use GenServer` {: .info}
210+
>
211+
> When you `use GenServer`, the `GenServer` module will
212+
> set `@behaviour GenServer` and define a `child_spec/1`
213+
> function, so your module can be used as a child
214+
> in a supervision tree.
215+
216+
Think of this summary as a ["Nutrition facts"](https://en.wikipedia.org/wiki/Nutrition_facts_label) for code generation. Keep in mind to only list changes made to the public API of the module. For example, if `use SomeModule` sets an internal attribute called `@_some_module_info` and this attribute is never meant to be public, it must not be listed.
217+
218+
For convenience, the markup notation to generate the admonition block above is:
219+
220+
```
221+
> #### `use GenServer` {: .info}
222+
>
223+
> When you `use GenServer`, the `GenServer` module will
224+
> set `@behaviour GenServer` and define a `child_spec/1`
225+
> function, so your module can be used as a child
226+
> in a supervision tree.
227+
```
204228

205229
### Avoid macros
206230

lib/ex_unit/lib/ex_unit/case.ex

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ defmodule ExUnit.Case do
2323
* `:register` - when `false`, does not register this module within
2424
ExUnit server. This means the module won't run when ExUnit suite runs.
2525
26-
This module automatically includes all callbacks defined in
27-
`ExUnit.Callbacks`. See that module for more information on `setup`,
28-
`start_supervised`, `on_exit` and the test process life cycle.
29-
30-
For grouping tests together, see `describe/2` in this module.
26+
> #### `use ExUnit.Case` {: .info}
27+
>
28+
> When you `use ExUnit.Case`, it will import the functionality
29+
> from `ExUnit.Assertions`, `ExUnit.Callbacks`, `ExUnit.DocTest`,
30+
> and this module itself.
3131
3232
## Examples
3333

lib/ex_unit/lib/ex_unit/case_template.ex

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ defmodule ExUnit.CaseTemplate do
2020
ExUnit.Case` under the hood. This means you can do things like `use
2121
MyCase, async: true`. You can also access this options in `using/2`.
2222
23+
> #### `use ExUnit.CaseTemplate` {: .info}
24+
>
25+
> When you `use ExUnit.CaseTemplate`, it will import the functionality
26+
> from `ExUnit.Assertions`, `ExUnit.Callbacks`, and this module itself.
27+
> It will also define a `__using__` callback, so the module itself can
28+
> be used as a template instead of `ExUnit.Case`.
29+
2330
## Example
2431
2532
defmodule MyCase do

0 commit comments

Comments
 (0)