Skip to content

Commit b919799

Browse files
committed
fix scoping docs
1 parent 45f5d82 commit b919799

File tree

1 file changed

+109
-158
lines changed

1 file changed

+109
-158
lines changed

doc/manual/variables-and-scoping.rst

+109-158
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,35 @@ introducing scope blocks are:
2626

2727
.. _man-scope-table:
2828

29-
+--------------------------------+----------------------------------------------------------------------------------+
30-
| Scope name | block/construct introducing this kind of scope |
31-
+================================+==================================================================================+
32-
| :ref:`global <man-global>` | module, baremodule, at interactive prompt (REPL) |
33-
+--------------------------------+------------------------------+---------------------------------------------------+
34-
| :ref:`local <man-local-scope>` | :ref:`soft <man-soft-scope>` | for, while, comprehensions, |
35-
| | | try-catch-finally, let |
36-
| +------------------------------+---------------------------------------------------+
37-
| | :ref:`hard <man-hard-scope>` | functions (either syntax, anonymous & do-blocks), |
38-
| | | type, immutable, macro |
39-
+--------------------------------+------------------------------+---------------------------------------------------+
40-
41-
Notably missing from this table are :ref:`begin blocks
42-
<man-compound-expressions>` and :ref:`if blocks
43-
<man-conditional-evaluation>`, which do *not* introduce new scope
44-
blocks. All three types of scopes follow somewhat different rules
45-
which will be explained below as well as some extra rules for
46-
certain blocks.
29+
* Scope blocks that may nest only in other global scope blocks:
30+
31+
- global scope
32+
33+
+ module, baremodule
34+
35+
+ at interactive prompt (REPL)
36+
37+
- local scope (don't allow nesting)
38+
39+
+ type, immutable, macro
40+
41+
* Scope blocks which may nest anywhere (in global or local scope):
42+
43+
- local scope
44+
45+
+ for, while, try-catch-finally, let
46+
47+
+ functions (either syntax, anonymous & do-blocks)
48+
49+
+ comprehensions, broadcast-fusing
50+
51+
52+
Notably missing from this table are
53+
:ref:`begin blocks <man-compound-expressions>`
54+
and :ref:`if blocks <man-conditional-evaluation>`,
55+
which do *not* introduce new scope blocks.
56+
Both types of scopes follow somewhat different rules
57+
which will be explained below.
4758

4859
Julia uses `lexical scoping <https://en.wikipedia.org/wiki/Scope_%28computer_science%29#Lexical_scoping_vs._dynamic_scoping>`_,
4960
meaning that a function's scope does not inherit from its caller's
@@ -117,24 +128,29 @@ changed within their global scope and not from an outside module.
117128
Note that the interactive prompt (aka REPL) is in the global scope of
118129
the module ``Main``.
119130

131+
Within the global scopes, the `global` keyword is never necessary,
132+
although allowed.
133+
134+
120135
.. _man-local-scope:
121136

122137
Local Scope
123138
-----------
124139

125-
A new local scope is introduced by most code-blocks, see above
126-
:ref:`table <man-scope-table>` for a complete list. A local scope
127-
*usually* inherits all the variables from its parent scope, both for
128-
reading and writing. There are two subtypes of local scopes, hard and
129-
soft, with slightly different rules concerning what variables are
130-
inherited. Unlike global scopes, local scopes are not namespaces,
140+
A new local scope is introduced by most code blocks (see above
141+
:ref:`table <man-scope-table>` for a complete list).
142+
A local scope inherits all the variables from a parent local scope,
143+
both for reading and writing.
144+
Additionally, the local scope inherits all globals that are assigned
145+
to in its parent global scope block (if it is surrounded by a global `if` or `begin` scope).
146+
Unlike global scopes, local scopes are not namespaces,
131147
thus variables in an inner scope cannot be retrieved from the parent
132148
scope through some sort of qualified access.
133149

134-
The following rules and examples pertain to both hard and soft local
135-
scopes. A newly introduced variable in a local scope does not
136-
back-propagate to its parent scope. For example, here the ``z`` is not
137-
introduced into the top-level scope:
150+
The following rules and examples pertain to local scopes.
151+
A newly introduced variable in a local scope does not
152+
back-propagate to its parent scope.
153+
For example, here the ``z`` is not introduced into the top-level scope:
138154

139155
.. doctest::
140156

@@ -155,17 +171,17 @@ using the ``local`` keyword:
155171

156172
.. doctest::
157173

158-
julia> x = 0;
174+
julia> x = 0
159175

160176
julia> for i = 1:10
161-
local x
177+
local x # this is the default
162178
x = i + 1
163179
end
164180

165181
julia> x
166182
0
167183

168-
Inside a local scope a new global variable can be defined using the
184+
Inside a local scope a global variable can be assigned to by using the
169185
keyword ``global``:
170186

171187
.. doctest::
@@ -183,7 +199,7 @@ keyword ``global``:
183199
parent local scope.
184200
185201
The location of both the ``local`` and ``global`` keywords within the
186-
scope block is irrelevant. The following is equivalent to the last
202+
scope block is irrelevant. The following is equivalent to the last
187203
example (although stylistically worse):
188204

189205
.. doctest::
@@ -196,71 +212,22 @@ example (although stylistically worse):
196212
julia> z
197213
10
198214

199-
Multiple global or local definitions can be on one line and can also
200-
be paired with assignments:
215+
Multiple global or local definitions can be on one line (or split across several lines)
216+
and can also be paired with assignments:
201217

202218
.. doctest::
203219

204220
julia> for i = 1:10
205221
global x = i, y, z
206-
local a = 4, b, c = 1
222+
local a = 4, b,
223+
c = 1
207224
end
208225

226+
Local scopes are introduced by most block keywords,
227+
with notable exceptions of `begin` and `if`.
209228

210-
.. _man-soft-scope:
211-
212-
Soft Local Scope
213-
^^^^^^^^^^^^^^^^
214-
215-
In a soft local scope, all variables are inherited from its parent
216-
scope unless a variable is specifically marked with the keyword
217-
``local``.
218-
219-
Soft local scopes are introduced by for-loops, while-loops,
220-
comprehensions, try-catch-finally-blocks, and let-blocks. There
221-
are some extra rules for :ref:`let-blocks <man-let-blocks>` and for
222-
:ref:`for-loops and comprehensions <man-for-loops-scope>`.
223-
224-
In the following example the ``x`` and ``y`` refer always to the same
225-
variables as the soft local scope inherits both read and write
226-
variables:
227-
228-
.. doctest::
229-
230-
julia> x, y = 0, 1;
231-
232-
julia> for i = 1:10
233-
x = i + y + 1
234-
end
235-
236-
julia> x
237-
12
238-
239-
Within soft scopes, the `global` keyword is never necessary, although
240-
allowed. The only case when it would change the semantics is
241-
(currently) a syntax error:
242-
243-
.. doctest::
244-
245-
julia> let
246-
local j = 2
247-
let
248-
global j = 3
249-
end
250-
end
251-
ERROR: syntax: `global j`: j is local variable in the enclosing scope
252-
...
253-
254-
.. _man-hard-scope:
255-
256-
Hard Local Scope
257-
^^^^^^^^^^^^^^^^
258-
259-
Hard local scopes are introduced by function definitions (in all their
260-
forms), type & immutable-blocks, and macro-definitions.
261-
262-
In a hard local scope, all variables are inherited from its parent
263-
scope unless:
229+
In a local scope, all variables are inherited from its parent
230+
global scope block unless:
264231

265232
- an assignment would result in a modified *global* variable, or
266233
- a variable is specifically marked with the keyword ``local``.
@@ -270,12 +237,12 @@ writing:
270237

271238
.. doctest::
272239

273-
julia> x, y = 1, 2;
240+
julia> x, y = 1, 2
274241

275242
julia> function foo()
276243
x = 2 # assignment introduces a new local
277-
return x + y # y refers to the global
278-
end;
244+
return x + y # y refers to the global
245+
end
279246

280247
julia> foo()
281248
4
@@ -285,69 +252,88 @@ writing:
285252

286253
An explicit ``global`` is needed to assign to a global variable:
287254

288-
.. doctest::
255+
.. sidebar:: Avoiding globals
289256

290-
julia> x = 1;
257+
Avoiding changing the value of global variables is considered by many
258+
to be a programming best-practice.
259+
One reason for this is that remotely changing the state of global variables in other
260+
modules should be done with care as it makes the local behavior of the program hard to reason about.
261+
This is why the scope blocks that introduce local scope require the ``global``
262+
keyword to declare the intent to modify a global variable.
291263

292-
julia> function foobar()
293-
global x = 2
294-
end;
264+
.. doctest::
295265

296266
julia> foobar();
297267

298268
julia> x
299269
2
300270

301-
Note that *nested functions* can behave differently to functions
302-
defined in the global scope as they can modify their parent scope's
271+
Note that *nested functions* can also modify their parent scope's
303272
*local* variables:
304273

305274
.. doctest::
306275

307-
julia> x, y = 1, 2;
276+
julia> x, y = 1, 2
308277

309-
julia> function baz()
278+
julia> function foo()
310279
x = 2 # introduces a new local
311280
function bar()
312281
x = 10 # modifies the parent's x
313282
return x + y # y is global
314283
end
315284
return bar() + x # 12 + 10 (x is modified in call of bar())
316-
end;
285+
end
317286

318-
julia> baz()
319-
22
287+
julia> foo()
288+
22 # (global x and y unchanged)
320289

321-
julia> x, y
322-
(1,2)
290+
The reason to allow *modifying local* variables of parent scopes in
291+
nested functions is to allow constructing `closures
292+
<https://en.wikipedia.org/wiki/Closure_%28computer_programming%29>`_
293+
which have a private state, for instance the ``state`` variable in the
294+
following example:
295+
296+
.. doctest::
297+
298+
julia> let
299+
state = 0
300+
global counter
301+
counter() = state += 1
302+
end
303+
304+
julia> counter()
305+
1
323306

324-
The distinction between inheriting global and local variables for
325-
assignment can lead to some slight differences between functions
326-
defined in local vs. global scopes. Consider the modification of the
307+
julia> counter()
308+
2
309+
310+
See also the closures in the examples in the next two sections.
311+
312+
The distinction between inheriting global scope and nesting local scope
313+
can lead to some slight differences between functions
314+
defined in local vs. global scopes for variable assignments.
315+
Consider the modification of the
327316
last example by moving ``bar`` to the global scope:
328317

329318
.. doctest::
330319

331-
julia> x, y = 1, 2;
332-
320+
julia> x, y = 1, 2
333321

334322
julia> function bar()
335323
x = 10 # local
336324
return x + y
337-
end;
325+
end
338326

339-
julia> function quz()
327+
julia> function foo()
340328
x = 2 # local
341329
return bar() + x # 12 + 2 (x is not modified)
342-
end;
343-
344-
julia> quz()
345-
14
330+
end
346331

347-
julia> x, y
348-
(1,2)
332+
julia> foo()
333+
14 # as x is not modified anymore.
334+
# (x and y unchanged)
349335

350-
Note that above subtlety does not pertain to type and macro
336+
Note that the above nesting rules do not pertain to type and macro
351337
definitions as they can only appear at the global scope.
352338
There are special scoping rules concerning the evaluation of default
353339
and keyword function arguments which are described in the
@@ -384,9 +370,9 @@ positive integers are even or odd:
384370

385371
.. doctest::
386372

387-
julia> even(n) = n == 0 ? true : odd(n-1);
373+
julia> even(n) = n == 0 ? true : odd(n - 1);
388374

389-
julia> odd(n) = n == 0 ? false : even(n-1);
375+
julia> odd(n) = n == 0 ? false : even(n - 1);
390376

391377
julia> even(3)
392378
false
@@ -396,43 +382,8 @@ positive integers are even or odd:
396382

397383
Julia provides built-in, efficient functions to test for oddness and evenness
398384
called :func:`iseven` and :func:`isodd` so the above definitions should only be
399-
taken as examples.
400-
401-
Hard vs. Soft Local Scope
402-
^^^^^^^^^^^^^^^^^^^^^^^^^
403-
404-
Blocks which introduce a soft local scope, such as loops, are
405-
generally used to manipulate the variables in their parent scope.
406-
Thus their default is to fully access all variables in their parent
407-
scope.
408-
409-
Conversely, the code inside blocks which introduce a hard local scope
410-
(function, type, and macro definitions) can be executed at any place in
411-
a program. Remotely changing the state of global variables in other
412-
modules should be done with care and thus this is an opt-in feature
413-
requiring the ``global`` keyword.
385+
taken as examples of scope, not usage.
414386

415-
The reason to allow *modifying local* variables of parent scopes in
416-
nested functions is to allow constructing `closures
417-
<https://en.wikipedia.org/wiki/Closure_%28computer_programming%29>`_
418-
which have a private state, for instance the ``state`` variable in the
419-
following example:
420-
421-
.. doctest::
422-
423-
julia> let
424-
state = 0
425-
global counter
426-
counter() = state += 1
427-
end;
428-
429-
julia> counter()
430-
1
431-
432-
julia> counter()
433-
2
434-
435-
See also the closures in the examples in the next two sections.
436387

437388
.. _man-let-blocks:
438389

0 commit comments

Comments
 (0)