You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: lib/elixir/pages/getting-started/module-attributes.md
+44-29Lines changed: 44 additions & 29 deletions
Original file line number
Diff line number
Diff line change
@@ -1,9 +1,10 @@
1
1
# Module attributes
2
2
3
-
Module attributes in Elixir serve two purposes:
3
+
Module attributes in Elixir serve three purposes:
4
4
5
-
1. They serve to annotate the module and its functions.
6
-
2. They work as a temporary module storage to be used during compilation.
5
+
1. as module and function annotations
6
+
2. as temporary module storage to be used during compilation
7
+
3. as compile-time constants
7
8
8
9
Let's check these examples.
9
10
@@ -76,6 +77,8 @@ defmodule MyServer do
76
77
end
77
78
```
78
79
80
+
> #### Newlines {: .warning}
81
+
>
79
82
> Do not add a newline between the attribute and its value, otherwise Elixir will assume you are reading the value, rather than setting it.
80
83
81
84
Trying to access an attribute that was not defined will print a warning:
@@ -115,7 +118,7 @@ end
115
118
116
119
This can be useful for pre-computing values and then injecting its results into the module. This is what we mean by temporary storage: after the module is compiled, the module attribute is discarded, except for the functions that have read the attribute. Note you cannot invoke functions defined in the same module as part of the attribute itself, as those functions have not yet been defined.
117
120
118
-
Every time an attribute is read inside a function, Elixir takes a snapshot of its current value. Therefore if you read the same attribute multiple times inside multiple functions, you end-up increasing compilation times as Elixir now has to compile every snapshot. Generally speaking, you want to avoid reading the same attribute multiple times and instead move it to function. For example, instead of this:
121
+
Every time we read an attribute inside a function, Elixir takes a snapshot of its current value. Therefore if you read the same attribute multiple times inside multiple functions, you end-up increasing compilation times as Elixir now has to compile every snapshot. Generally speaking, you want to avoid reading the same attribute multiple times and instead move it to function. For example, instead of this:
> You may want to use module attributes as constants for plain data types. However, functions themselves are sufficient for this role. For example, instead of defining:
136
-
>
137
-
> ```elixir
138
-
>@hours_in_a_day24
139
-
> ```
140
-
>
141
-
>You should prefer:
142
-
>
143
-
> ```elixir
144
-
>defphours_in_a_day(), do:24
145
-
> ```
146
-
>
147
-
>You may even define a public function if it needs to be shared across modules. It is common in many projects to have a module called `MyApp.Constants` that defines all constants used throughout the codebase.
148
-
>
149
-
>This holds truefor any data structure, as long as they don't have any expression in them. For example, you may specify a system configuration constant as follows:
> Given data structures in Elixir are immutable, only a single instance of the data structure above is allocated and shared across all functions calls, behaving precisely as a constant, as long as it doesn't have any executable expression.
156
-
>
157
-
>Theusecasefor module attributes arise when you need to do some work at compile-time and then inject its results inside a function. They may also be useful for defining constants to be used in patterns or guards, as an alternative to `defguard/1`.
136
+
## As compile-time constants
137
+
138
+
Module attributes may also be useful as compile-time constants. Generally speaking, functions themselves are sufficient for the role of constants in a codebase. For example, instead of defining:
139
+
140
+
```elixir
141
+
@hours_in_a_day24
142
+
```
143
+
144
+
You should prefer:
145
+
146
+
```elixir
147
+
defphours_in_a_day(), do:24
148
+
```
149
+
150
+
You may even define a public function if it needs to be shared across modules. It is common in many projects to have a module called `MyApp.Constants` that defines all constants used throughout the codebase.
151
+
152
+
You can even have composite data structures as constants, as long as they are made exclusively of other data types (no function calls, no operators, and no other expressions). For example, you may specify a system configuration constant as follows:
Given data structures in Elixir are immutable, only a single instance of the data structure above is allocated and shared across all functions calls, as long as it doesn't have any executable expression.
159
+
160
+
The use case for module attributes arise when you need to do some work at compile-time and then inject its results inside a function. A common scenario is module attributes inside patterns and guards (as an alternative to `defguard/1`), since they only support a limited set of expressions:
161
+
162
+
```elixir
163
+
# Inside pattern
164
+
@default_timezone"Etc/UTC"
165
+
defshift(@default_timezone), do:...
166
+
167
+
# Inside guards
168
+
@time_periods [:am, :pm]
169
+
defshift(time, period) when period in@time_periods, do:...
170
+
```
171
+
172
+
Module attributes as constants and as temporary storage are most often used together: the module attribute is used to compute and store an expensive value, and then exposed as constant from that module.
0 commit comments