-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathscope.tex
738 lines (637 loc) · 41.9 KB
/
scope.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
%Part{Scope, Root = "CLM.MSS"}
%Chapter of Common Lisp Manual. Copyright 1984, 1988, 1989 Guy L. Steele Jr.
\clearpage\def\pagestatus{FINAL PROOF}
\ifx \rulang\Undef
\chapter{Scope and Extent}
\label{SCOPE}
In describing various features of the Common Lisp language, the notions of
\emph{scope} and \emph{extent} are frequently useful. These notion arise when
some object or construct must be referred to from some distant part of a
program. \emph{Scope} refers to the spatial or textual region of the
program within which references may occur. \emph{Extent} refers to the
interval of time during which references may occur.
As a simple example, consider this program:
\begin{lisp}
(defun copy-cell (x) (cons (car x) (cdr x)))
\end{lisp}
The scope of the parameter named \cdf{x} is the body of the \cdf{defun} form.
There is no way to refer to this parameter from any other place but within
the body of the \cdf{defun}. Similarly, the extent of the parameter \cdf{x}
(for any particular call to \cdf{copy-cell}) is the interval from the time
the function is invoked to the time it is exited. (In the general case,
the extent of a parameter may last beyond the time of function exit,
but that cannot occur in this simple case.)
Within Common Lisp, a referenceable entity is \emph{established} by the execution
of some language construct, and the scope and extent of the entity are
described relative to the construct and the time (during execution of the
construct) at which the entity is established.
For the purposes of this discussion, the term ``entity'' refers not only
to Common Lisp data objects, such as symbols and conses, but also to
variable bindings (both ordinary and special), catchers,
and \cdf{go} targets. It is important to distinguish between
an entity and a name for the entity. In a function definition
such as
\begin{lisp}
(defun foo (x y) (* x (+ y 1)))
\end{lisp}
there is a single name, \cdf{x}, used to refer to the first parameter
of the procedure whenever it is invoked; however, a new binding
is established on every invocation. A \emph{binding} is a particular
parameter instance. The value of a reference to the name \cdf{x}
depends not only on the scope within which it occurs (the one in
the body of \cdf{foo} in the example occurs in the scope of the
function definition's parameters) but also on the particular
binding or instance involved. (In this case, it depends on the
invocation during which
the reference is made). More complicated examples
appear at the end of this chapter.
There are a few kinds of scope and extent that are particularly useful
in describing Common Lisp:
\begin{itemize}
\item
\emph{Lexical scope}. Here references to the established
entity can occur only within certain program portions that are
lexically (that is, textually) contained within the establishing construct.
Typically the construct will have a part designated the \emph{body},
and the scope of all entities established will be (or include) the body.
Example: the names of parameters to a function normally are lexically scoped.
\item
\emph{Indefinite scope}. References may occur anywhere, in any program.
\item
\emph{Dynamic extent}. References may occur at any time in the interval
between establishment of the entity and the explicit disestablishment
of the entity. As a rule, the entity is disestablished when execution
of the establishing construct completes or is otherwise terminated.
Therefore entities with dynamic extent obey a stack-like discipline,
paralleling the nested executions of their establishing constructs.
Example: the \cdf{with-open-file} construct opens a connection to a file
and creates a stream object to represent the connection. The stream object
has indefinite extent, but the connection to the open file has dynamic extent:
when control exits the \cdf{with-open-file} construct, either normally
or abnormally, the stream is automatically closed.
Example: the binding of a ``special'' variable has dynamic extent.
\item
\emph{Indefinite extent}. The entity continues to exist as long as the
possibility of reference remains. (An implementation is free to
destroy the entity if it can prove that reference to it is no longer possible.
Garbage collection strategies implicitly employ such proofs.)
Example: most Common Lisp data objects have indefinite extent.
Example: the bindings of lexically scoped parameters of a function have
indefinite extent. (By contrast, in Algol the bindings of lexically scoped
parameters of a procedure have dynamic extent.)
The function definition
\begin{lisp}
(defun compose (f g) \\*
~~\#'(lambda (x) \\*
~~~~~~(funcall f (funcall g x))))
\end{lisp}
when given two arguments, immediately returns a function as its value.
The parameter bindings for \cdf{f} and \cdf{g} do not disappear because the
returned function, when called, could still refer to those bindings.
Therefore
\begin{lisp}
(funcall (compose \#'sqrt \#'abs) -9.0)
\end{lisp}
produces the value \cd{3.0}. (An analogous procedure would not necessarily work
correctly in typical Algol implementations or, for that matter,
in most Lisp dialects.)
\end{itemize}
In addition to the above terms, it is convenient to define \emph{dynamic scope}
to mean \emph{indefinite scope and dynamic extent}. Thus we speak of
``special'' variables as having dynamic scope, or being dynamically scoped,
because they have indefinite scope and dynamic extent: a special variable
can be referred to anywhere as long as its binding is currently
in effect.
The term ``dynamic scope'' is a misnomer. Nevertheless
it is both traditional and useful.
The above definitions do not take into account the possibility of
\emph{shadowing}. Remote reference of entities is accomplished by using
\emph{names} of one kind or another. If two entities have the same name,
then the second may shadow the first, in which case an occurrence
of the name will refer to the second and cannot refer to the first.
In the case of lexical scope,
if two constructs that establish entities
with the same name are textually nested, then references within the inner
construct refer to the entity established by the inner one; the inner one
shadows the outer one. Outside the inner construct but inside the outer one,
references refer to the entity established by the outer construct.
For example:
\begin{lisp}
(defun test (x z) \\
~~(let ((z (* x 2))) (print z)) \\
~~z)
\end{lisp}
The binding of the variable \cdf{z} by the \cdf{let} construct shadows
the parameter binding for the function \cdf{test}. The reference to the
variable \cdf{z} in the \cdf{print} form refers to the \cdf{let} binding.
The reference to \cdf{z} at the end of the function refers to the parameter
named \cdf{z}.
In the case of dynamic extent, if the time intervals of two entities
overlap, then one interval will necessarily be nested within the
other one. This is a property of the design of Common Lisp.
\beforenoterule
\begin{implementation}
Behind the assertion that dynamic extents nest properly
is the assumption that there is only a single program or process.
Common Lisp does not address the problems of multiprogramming
(timesharing) or
multiprocessing (more than one active processor)
within a single Lisp environment. The documentation for
implementations that extend Common Lisp for multiprogramming or
multiprocessing should
be very clear on what modifications are induced by such extensions
to the rules of extent and scope.
Implementors should note that Common Lisp has been carefully designed
to allow special variables to be implemented using either
the ``deep binding'' technique or the ``shallow binding'' technique,
but the two techniques have different semantic
and performance implications for multiprogramming and multiprocessing.
\end{implementation}
\afternoterule
A reference by name to an entity with dynamic extent
will always refer to the entity of that name
that has been most recently established
that has not yet been disestablished.
For example:
\begin{lisp}
(defun fun1 (x) \\
~~(catch 'trap (+ 3 (fun2 x)))) \\
\\
(defun fun2 (y) \\
~~(catch 'trap (* 5 (fun3 y)))) \\
\\
(defun fun3 (z) \\
~~(throw 'trap z))
\end{lisp}
Consider the call \cd{(fun1 7)}. The result will be \cd{10}. At the time
the \cdf{throw} is executed, there are two outstanding catchers with the
name \cdf{trap}: one established within procedure \cd{fun1}, and the other
within procedure \cd{fun2}. The latter is the more recent, and so the
value \cd{7} is returned from the \cdf{catch} form in \cd{fun2}.
Viewed from within \cd{fun3}, the \cdf{catch} in \cd{fun2} shadows the one in \cd{fun1}.
Had \cd{fun2}
been defined as
\begin{lisp}
(defun fun2 (y) \\
~~(catch 'snare (* 5 (fun3 y))))
\end{lisp}
then the two catchers would have different names, and therefore the one
in \cd{fun1} would not be shadowed. The result would then have been \cd{7}.
As a rule, this book simply speaks of the scope or extent of an entity;
the possibility of shadowing is left implicit.
\begin{itemize}
\item
Variable bindings normally have lexical scope and indefinite extent.
\end{itemize}
\begin{itemize}
\item Variable bindings for which there is a \cdf{dynamic-extent}
declaration also have lexical scope and indefinite extent,
but objects that are the values of such bindings may have
dynamic extent.
(The declaration is the programmer's guarantee that
the program will behave correctly even if certain of the data objects have only
dynamic extent rather than the usual indefinite extent.)
\item Bindings of variable names to symbol macros by
\cdf{symbol-macrolet} have lexical scope and indefinite extent.
\end{itemize}
\begin{itemize}
\item
Variable bindings that are declared to be \cdf{special} have dynamic scope
(indefinite scope and dynamic extent).
\end{itemize}
\begin{itemize}
\item Bindings of function names established, for example, by \cdf{flet} and
\cdf{labels} have lexical scope and indefinite extent.
\item Bindings of function names for which there is a \cdf{dynamic-extent}
declaration also have lexical scope and indefinite extent,
but function objects that are the values of such bindings may have
dynamic extent.
\item Bindings of function names to macros as established by
\cdf{macrolet} have lexical scope and indefinite extent.
\item Condition handlers and restarts have dynamic scope
(see chapter~\ref{CONDITION}).
\end{itemize}
\begin{itemize}
\item
A catcher established by a \cdf{catch}
or \cdf{unwind-protect} special operator has dynamic
scope.
\item
An exit point established by a \cdf{block} construct has lexical
scope and dynamic extent. (Such exit points are also established
by \cdf{do}, \cdf{prog}, and other iteration constructs.)
\item
The \cdf{go} targets
established by a \cdf{tagbody}, named by the tags in the \cdf{tagbody},
and referred to by \cdf{go}
have lexical scope and dynamic extent. (Such \cdf{go} targets
may also appear as tags in the bodies of
\cdf{do}, \cdf{prog}, and other iteration constructs.)
\item
Named constants such as \cdf{nil} and \cdf{pi} have indefinite
scope and indefinite extent.
\end{itemize}
The rules of lexical scoping imply that lambda-expressions
appearing in the \cdf{function} construct will,
in general, result in ``closures''
over those non-special variables visible to the lambda-expression.
That is, the function represented by a lambda-expression
may refer to any lexically apparent non-special variable and get the
correct value, even if the construct that established the binding
has been exited in the course of execution.
The \cdf{compose} example shown earlier in this chapter
provides one illustration of this.
The rules also imply that special variable bindings are not
``closed over'' as they may be in certain other dialects of Lisp.
Constructs that use lexical scope effectively
generate a new name for each established entity on each execution.
Therefore dynamic shadowing cannot occur (though lexical shadowing may).
This is of particular importance when dynamic extent is involved.
For example:
\begin{lisp}
(defun contorted-example (f g x) \\
~~(if (= x 0) \\
~~~~~~(funcall f) \\
~~~~~~(block here \\
~~~~~~~~~(+ 5 (contorted-example g \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\#'(lambda () \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(return-from here 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(- x 1))))))
\end{lisp}
Consider the call \cd{(contorted-example nil nil 2)}. This produces
the result \cd{4}. During the course of execution, there are three
calls on \cdf{contorted-example}, interleaved with two establishments
of blocks:
\begin{lisp}
(contorted-example nil nil 2) \\
\\
~~(block here${}_1$ ...) \\
\\
~~~~(contorted-example nil \#'(lambda () (return-from here${}_1$ 4)) 1) \\
\\
~~~~~~(block here${}_2$ ...) \\
\\
~~~~~~~~(contorted-example \#'(lambda () (return-from here${}_1$ 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~\#'(lambda () (return-from here${}_2$ 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~0) \\
~~~~~~~~~~(funcall f) \\
~~~~~~~~~~~~~~~~\textrm{where} f \EV\ \#'(lambda () (return-from here${}_1$ 4)) \\
\\
~~~~~~~~~~~~(return-from here${}_1$ 4)
\end{lisp}
At the time the \cdf{funcall} is executed
there are two \cdf{block} exit points outstanding, each apparently
named \cdf{here}. In the trace above, these exit points are distinguished
for expository purposes by subscripts.
The \cdf{return-from} form executed as a result of the \cdf{funcall}
operation
refers to the \emph{outer} outstanding exit point
(\cd{here${}_1$}), not the
inner one (\cd{here${}_2$}).
This is a consequence of the rules of lexical scoping: it
refers to that exit point textually visible at the point of
execution of the \cdf{function}
construct (here abbreviated by the \cd{\#'} syntax) that resulted
in creation of the function object actually invoked by the \cdf{funcall}.
If, in this example, one were to change the form \cd{(funcall f)} to
\cd{(funcall g)}, then the value of the call \cd{(contorted-example nil nil 2)}
would be \cd{9}. The value would change because the \cdf{funcall} would cause the
execution of \cd{(return-from here${}_2$ 4)}, thereby causing
a return from the inner exit point (\cd{here${}_2$}).
When that occurs, the value \cd{4} is returned from the
middle invocation of \cdf{contorted-example}, \cd{5} is added to that
to get \cd{9}, and that value is returned from the outer block
and the outermost call to \cdf{contorted-example}. The point
is that the choice of exit point returned from has nothing to do with its
being innermost or outermost; rather,
it depends on the lexical scoping information
that is effectively packaged up with a lambda-expression when the
\cdf{function} construct is executed.
This function \cdf{contorted-example} works only because the
function named by \cdf{f} is invoked during the extent of the exit point.
Block exit points are like non-special variable bindings in having
lexical scope, but they differ in having dynamic extent rather than indefinite
extent. Once the flow of execution has left the block construct,
the exit point is disestablished. For example:
\begin{lisp}
(defun illegal-example () \\
~~(let ((y (block here \#'(lambda (z) (return-from here z))))) \\
~~~~(if (numberp y) y (funcall y 5))))
\end{lisp}
One might expect the call \cd{(illegal-example)} to produce \cd{5}
by the following incorrect reasoning:
the \cdf{let} statement binds the variable \cdf{y} to the
value of the \cdf{block} construct; this value is a function resulting
from the lambda-expression. Because \cdf{y} is not a number, it is
invoked on the value \cd{5}. The \cdf{return-from} should then
return this value from the exit point named \cdf{here}, thereby
exiting from the block \emph{again} and giving \cdf{y} the value \cd{5}
which, being a number, is then returned as the value of the call
to \cdf{illegal-example}.
The argument fails only because exit points are defined in Common Lisp
to have dynamic extent. The argument is correct up to the execution
of the \cdf{return-from}. The execution of the \cdf{return-from} is
an error, however, \emph{not} because it cannot refer to the exit point,
but because it does correctly refer to an exit point \emph{and}
that exit point has been disestablished.
%RUSSIAN
\else
\chapter{Область и продолжительность видимости}
\label{SCOPE}
При описании различных возможностей Common Lisp'а очень важными понятиями
являются \emph{области и продолжительности видимости}. Эти понятия возникают,
когда к некоторому объекту или конструкции необходимо обратиться из некоторой
части кода. \emph{Область видимости} объектов отмечает пространственный или
текстовый регион, в котором находящаяся внутри программа может обращаться к этим
объектам. \emph{Продолжительность видимости} обозначает временной интервал, в
течение которого программа может обращаться к данным объектам.
Вот простой пример такой программы:
\begin{lisp}
(defun copy-cell (x) (cons (car x) (cdr x)))
\end{lisp}
Областью видимости параметра с именем \cdf{x} является тело формы \cdf{defun}.
Способа сослаться на этот параметр из какого-либо другого места программы
нет. Продолжительностью видимости параметра \cdf{x} (для какого-нибудь вызова
\cdf{copy-cell}) является интервал времени, начиная с вызова функции и заканчивая
выходом из неё. (В общем случае продолжительность видимости параметра может
продлиться и после завершения функции, но в данном простом случае такого не
может быть.)
В Common Lisp сущность, на которую можно сослаться из кода, \emph{создаётся} с
помощью специальных языковых конструкций, и область и продолжительность
видимости описываются в зависимости от этой конструкции и времени (выполнения
конструкции) в которое эта сущность была создана.
Для предмета данного описания, термин <<сущность>> указывает не только на
объекты Common Lisp'а такие как символы и cons-ячейки, но и также на связывания
переменных (обычных и специальных), ловушки, и \cd{метки переходов}. Важно
отметить различие между сущностью и именем для этой сущности. В определение
функции, такой как:
\begin{lisp}
(defun foo (x y) (* x (+ y 1)))
\end{lisp}
существует только одно имя, \cdf{x}, используемое для ссылки на первый параметр
процедуры, когда бы они не была вызвана. \emph{Связывание} --- это, в частности,
экземпляр параметра. Значение связанное с именем \cdf{x} зависит не только от
области видимости, в которой данная связь возникла (в данном примере в теле
функции \cdf{foo} связь возникла в области видимости определения параметров
функции), но также, в частности, от механизма связывания. (В данном случае,
значение зависит от вызова функции, в течение которого создаётся ссылка). Более
сложный пример приводится в конце данной главы.
Вот некоторые виды областей и продолжительностей видимости, которые, в частности,
полезны при описании Common Lisp'а:
\begin{itemize}
\item
\emph{Лексическая область видимости}. В ней связи к установленным сущностям могут
использоваться только в той части программы, которая лексически (т.е. текст
программы) находится в конструкции устанавливающей данные связи. Обычно эта
конструкция будет содержать часть, определённую как \emph{тело (body)}, и область
видимости всех сущностей будет установлена только в этом теле.
Например: имена параметров в функции обычно ограничиваются лексической областью
видимости.
\item
\emph{Неограниченная область видимости}. Ссылка на сущность может производится в
любом месте программы.
\item
\emph{Динамическая продолжительность видимости}. Ссылки на сущности могут
производится в любое время на интервале между установкой сущности и явного
упразднения сущности. Как правило, сущность упраздняется, когда выполнение конструкции
завершается или как-либо прерывается. Таким образом, сущности с динамической
продолжительностью видимости подчиняются механизму типа стек, они распараллеливают
выполнение кода, вложенного в их конструкции.
Например: \cdf{with-open-file} открывает соединение с файлом и создаёт объект
потока для отображения этого соединения. Объект потока имеет неограниченную область
видимости, но соединение с открытым файлом имеет динамическую продолжительность
видимости: когда выполнение в любом, нормальном или аварийном случае, выйдет за
рамки конструкции \cdf{with-open-file}, поток будет автоматически закрыт.
Например: связывание <<специальной (special)>> переменной имеет динамическую
продолжительность видимости.
\item
\emph{Неограниченная продолжительности видимости}. Сущность продолжает существовать
пока существует возможность ссылаться на неё. (Реализации разрешается удалить
сущность, если она может доказать, что ссылка на неё более невозможна. Стратегии
сборщика мусора неявно используют такие доказательства.)
Например: большинство Common Lisp объектов имеют неограниченную продолжительность
видимости.
Например: связывание лексически замкнутых параметров функции имеет неограниченную
продолжительность видимости. (В отличие от Algol'а, где связывание лексически
замкнутых параметров процедуры имеют динамическую продолжительность видимости.)
Определение функции
\begin{lisp}
(defun compose (f g) \\*
~~\#'(lambda (x) \\*
~~~~~~(funcall f (funcall g x))))
\end{lisp}
при получении двух параметров, немедленно возвращает функции в качестве
результата.
Связи параметров для \cdf{f} и \cdf{g} не теряются, потому что возвращённая
функция, когда будет вызвана, будет продолжать ссылаться на эти связи.
Таким образом
\begin{lisp}
(funcall (compose \#'sqrt \#'abs) -9.0)
\end{lisp}
вернёт значение \cd{3.0}. (Аналогичная процедура не захочет корректно работать в
типичной реализации Algol'а или, даже, в большинстве диалектов Lisp'а.)
\end{itemize}
В дополнение к вышеназванным терминам, удобно определить \emph{динамическую
область видимости}, которая означает \emph{неограниченную область видимости и
динамическую продолжительность видимости}. Следовательно мы говорим о
<<специальных (special)>> переменных, как об имеющих динамическую область
видимости или будучи динамически замкнутых FIXME, потому что они имеют
неограниченную область видимости и динамическую продолжительность видимости:
к специальным переменным можно сослаться из любой точки программы на протяжении
существования их связываний.
Термин <<динамическая область видимости>> некорректен. Как бы то ни было это и
устоялось, и удобно.
Сказанное выше не рассматривает возможность \emph{скрытия (shadowing)}. Далёкие
(FIXME) ссылки на сущности осуществляются с
использованием \emph{имён} того или иного типа. Если две сущности имеют
одинаковое имя, тогда второе имя может скрыть первое, в таком случае ссылка с
помощью этого имени будет осуществлена на вторую сущность и не может быть
осуществлена на первую.
В случае лексической области видимости,
если две конструкции, которые устанавливают сущности с одинаковыми именами,
расположены в тексте одна внутри другой, тогда ссылки внутри внутренней
конструкции указывают на сущности внутренней конструкции, то есть внутренние
сущности скрывают внешние. Вне внутренней конструкции, но внутри внешней
конструкции ссылки указывают на сущности, установленные внешней конструкцией.
\begin{lisp}
(defun test (x z) \\
~~(let ((z (* x 2))) (print z)) \\
~~z)
\end{lisp}
Связывание переменной \cdf{z} с помощью конструкции \cdf{let} скрывает связывание
одноимённого параметра функции \cdf{test}. Ссылка на переменную \cdf{z} в форме
\cdf{print} указывает на \cdf{let} связывание. Ссылка на \cdf{z} в конце функции
указывает на параметр с именем \cdf{z}.
В случае динамической продолжительности видимости, если временные интервалы двух
сущностей перекрываются, тогда они будут обязательно вложенными один в другого. Это
свойство Common Lisp дизайна.
Ссылка по имени на сущность с динамической продолжительностью жизни всегда
указывает на сущность с этим именем, что была установлена наипозднейшей и ещё не
была упразднена.
Например:
\begin{lisp}
(defun fun1 (x) \\
~~(catch 'trap (+ 3 (fun2 x)))) \\
\\
(defun fun2 (y) \\
~~(catch 'trap (* 5 (fun3 y)))) \\
\\
(defun fun3 (z) \\
~~(throw 'trap z))
\end{lisp}
Рассмотрим вызов \cd{(fun1 7)}. Результатом будет \cd{10}. Во время выполнения
\cdf{throw}, существует две ловушки с именем \cdf{trap}: одна установлена в
процедуре \cd{fun1}, и другая --- в \cd{fun2}. Более поздняя в \cd{fun2}, и
тогда, из формы \cdf{catch}, что в \cd{fun2}, возвращается значение \cd{7}.
Рассматриваемая из \cd{fun3}, \cdf{catch} в \cd{fun2} скрывает одноимённую в
\cd{fun1}.
Если бы \cd{fun2} была определена как
\begin{lisp}
(defun fun2 (y) \\
~~(catch 'snare (* 5 (fun3 y))))
\end{lisp}
тогда бы две ловушки имели разные имена, и в таком случае одна из них из
\cd{fun1} не была бы скрыта. Результатом бы стало \cd{7}.
Как правило, данная книга по простому рассказывает об областях видимости и
продолжительности сущности, возможность скрытия оставляется без рассмотрения.
Далее важные правила области и продолжительности видимости в Common Lisp'е:
\begin{itemize}
\item Связывания переменных обычно имеют лексическую область видимости и неограниченную
продолжительность видимости.
\end{itemize}
\begin{itemize}
\item Связывания переменных, для которых декларировано \cdf{dynamic-extent} также
имеют лексическую область видимости и неограниченную продолжительность, но
объекты, которые являются значениями этих связываний могут иметь динамическую
продолжительность видимости.
\item Связывания имён переменных с символом макроса с помощью
\cdf{symbol-macrolet} имеют лексическую область видимости и неограниченную
продолжительность видимости.
\end{itemize}
\begin{itemize}
\item
Связывания переменных, которые задекларированы быть \cd{специальными (special)},
имеют динамическую область видимости (неограниченную область видимости и
динамическую продолжительность).
\end{itemize}
\begin{itemize}
\item Связывания имён функций устанавливаются, например, формами \cdf{flet} и
\cdf{labels} и имеют лексическую область видимости и неограниченную продолжительность.
\item Связывания имён функций, для которых задекларировано \cdf{dynamic-extent},
также имеют лексическую область видимости и неограниченную продолжительность,
но объекты функции, которые являются значениями для данных связываний могут
иметь динамическую продолжительность видимости.
\item Связывания имён функций с макросами, установленными с помощью
\cdf{macrolet} имеют лексическую область видимости и неограниченную
продолжительность.
\item Обработчики условий и перезапусков (condition and restarts) имеют
динамическую область видимости (смотрите главу~\ref{CONDITION}).
\end{itemize}
\begin{itemize}
\item Ловушка установленная с помощью операторов \cdf{catch} или
\cdf{unwind-protect} имеет динамическую область видимости.
\item Точка выхода установленная с помощью конструкции \cdf{block} имеет лексическую
область видимости и динамическую продолжительность. (Такие точки выхода, также
устанавливаются с помощью \cdf{do}, \cdf{prog} и другими конструкциями для итераций.)
\item Цели для \cdf{go}, устанавливающиеся с помощью \cdf{tagbody} и именующиеся с
помощью тегов в \cdf{tagbody}, на которые указывает \cdf{go}, имеют лексическую
область видимости и динамическую продолжительность. (Такие \cdf{go} цели могут
также появляться как теги в телах \cdf{do}, \cdf{prog} и других конструкций для итераций.)
\item Именованные константы, такие как \cdf{nil} и \cdf{pi} имеют неограниченную область
видимости и неограниченную продолжительность.
\end{itemize}
Правила для лексической области видимости подразумевают, что лямбда-выражения
(анонимные функции), появляющиеся в \cdf{function}, будут
являться <<замыканиями>> над этими неспециальными (non-special) переменными,
которые видимы для лямбда-выражения.
Это значит, что функция предоставленная лямбда-выражением может ссылаться на
любую лексически доступную неспециальную (non-special) переменную и получать
корректное значение, даже если выполнение уже вышло из конструкции, которая
устанавливала связи.
Пример \cdf{compose}, рассмотренный в данной главе ранее, предоставлял
изображение такого механизма.
Правила также подразумевают, что связывания специальных переменных не
<<замыкаются>>, как может быть в некоторых других диалектах Lisp'а.
Конструкции, которые используют лексическую область видимости генерируют новое имя
для каждой устанавливаемой сущности при каждом исполнении.
Таким образом, динамическое скрытие не может произойти (тогда как лексическое
может).
Это, в частности, важно, когда используется динамическая продолжительность
видимости.
Например:
\begin{lisp}
(defun contorted-example (f g x) \\
~~(if (= x 0) \\
~~~~~~(funcall f) \\
~~~~~~(block here \\
~~~~~~~~~(+ 5 (contorted-example g \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\#'(lambda () \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(return-from here 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~(- x 1))))))
\end{lisp}
Рассмотрим вызов \cd{(contorted-example nil nil 2)}. Он вернёт результат
\cd{4}. Во время исполнения, \cdf{contorted-example} будет вызвана три раза,
чередуясь с двумя блоками:
\begin{lisp}
(contorted-example nil nil 2) \\
\\
~~(block here${}_1$ ...) \\
\\
~~~~(contorted-example nil \#'(lambda () (return-from here${}_1$ 4)) 1) \\
\\
~~~~~~(block here${}_2$ ...) \\
\\
~~~~~~~~(contorted-example \#'(lambda () (return-from here${}_1$ 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~\#'(lambda () (return-from here${}_2$ 4)) \\
~~~~~~~~~~~~~~~~~~~~~~~~~~~0) \\
~~~~~~~~~~(funcall f) \\
~~~~~~~~~~~~~~~~\textrm{where} f \EV\ \#'(lambda () (return-from here${}_1$ 4)) \\
\\
~~~~~~~~~~~~(return-from here${}_1$ 4)
\end{lisp}
В время выполнения \cdf{funcall} существует две невыполненные точки выхода
\cdf{block}, каждая с именем \cdf{here}. В стеке вызовов выше, эти две точки
для наглядности проиндексированы.
Форма \cdf{return-from}, выполненная как результат операции \cdf{funcall},
ссылается на \emph{внешнюю} невыполненную точку выхода (\cd{here${}_1$}), но не
на (\cd{here${}_2$}).
Это следствие правил лексических областей видимости: форма ссылается на ту точку
выхода, что видима по тексту в точке вызова создания функции
(здесь отмеченной с помощью синтаксиса \cd{\#'}). (FIXME)
Если в данном примере, изменить форму \cd{(funcall f)} на \cd{(funcall g)},
тогда значение вызова \cd{(contorted-example nil nil 2)} будет \cd{9}. Значение
измениться по сравнению с предыдущим разом, потому что \cdf{funcall} вызовет
выполнение \cd{(return-from here${}_2$ 4)}, и это в свою очередь вызовет выход
из внутренней точки выхода (\cd{here${}_2$}).
Когда это случиться, значение \cd{4} будет возвращено из середины вызова
\cdf{contorted-example}, к нему добавится \cd{5} и результат окажется \cd{9}, и это
значение вернётся из внешнего блока и вообще из вызова
\cdf{contorted-example}. Цель данного примера, показать что выбор точки выхода
зависит от лексической области, которая была
захвачена лямбда-выражением, когда вызывался код создания этой анонимной функции.
Эта функция \cdf{contorted-example} работает только потому, что функция с именем
\cdf{f} вызывается в процессе продолжительности действия точки выхода.
Точки выхода из блока ведут себя, как связывания неспециальных (non-special)
переменных в имеющимся лексическом окружении, но отличаются тем, что имеют
динамическую продолжительность видимости, а не неограниченную. Как только
выполнение покинет блок с этой точкой выхода, она перестанет
существовать. Например:
\begin{lisp}
(defun illegal-example () \\
~~(let ((y (block here \#'(lambda (z) (return-from here z))))) \\ф
~~~~(if (numberp y) y (funcall y 5))))
\end{lisp}
Можно предположить, что вызов \cd{(illegal-example)} вернёт \cd{5}:
Форма \cdf{let} связывает переменную \cdf{y} со значением выполнения конструкции
\cdf{block}; её значение получится равным анонимной функции. Так как \cdf{y} не
является числом, она вызывается с параметром \cd{5}. \cdf{return-from} тогда
должны вернуть данное значение с помощью точки выхода \cdf{here}, тогда
осуществляется выход из блока \emph{ещё раз} и \cdf{y} получает значение \cd{5},
которое будучи числом, возвращается в качестве значения для \cdf{illegal-example}.
Рассуждения выше неверны, потому что точки выхода определяемые в
Common Lisp'е имеют динамическую продолжительность видимости. Аргументация верна
только до вызова \cdf{return-from}. Вызов формы \cdf{return-from} является
ошибкой, \emph{не потому что} она не может сослаться на точку выхода, а потому
что она корректно ссылается на точку выхода \emph{и} эта точка выхода уже была
упразднена.
\fi