/
phasers.pod6
430 lines (292 loc) · 15.6 KB
/
phasers.pod6
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
=begin pod :tag<perl6>
=TITLE Phasers
=SUBTITLE Program execution phases and corresponding phaser blocks
The lifetime (execution timeline) of a program is broken up into phases. A
I<phaser> is a block of code called during a specific execution phase.
=head1 Phasers
A phaser block is just a trait of the closure containing it, and is
automatically called at the appropriate moment. These auto-called blocks are
known as I<phasers>, since they generally mark the transition from one phase of
computing to another. For instance, a C<CHECK> block is called at the end of
compiling a compilation unit. Other kinds of phasers can be installed as well;
these are automatically called at various times as appropriate, and some of
them respond to various control exceptions and exit values.
Here is a summary:
=begin code :skip-test
BEGIN {...} # * at compile time, ASAP, only ever runs once
CHECK {...} # * at compile time, ALAP, only ever runs once
INIT {...} # * at run time, ASAP, only ever runs once
END {...} # at run time, ALAP, only ever runs once
ENTER {...} # * at every block entry time, repeats on loop blocks.
LEAVE {...} # at every block exit time (even stack unwinds from exceptions)
KEEP {...} # at every successful block exit, part of LEAVE queue
UNDO {...} # at every unsuccessful block exit, part of LEAVE queue
FIRST {...} # at loop initialization time, before any ENTER
NEXT {...} # at loop continuation time, before any LEAVE
LAST {...} # at loop termination time, after any LEAVE
PRE {...} # assert precondition at every block entry, before ENTER
POST {...} # assert postcondition at every block exit, after LEAVE
CATCH {...} # catch exceptions, before LEAVE
CONTROL {...} # catch control exceptions, before LEAVE
LAST {...} # supply tapped by whenever-block is done, runs very last
QUIT {...} # catch async exceptions within a whenever-block, runs very last
COMPOSE {...} # when a role is composed into a class
CLOSE {...} # appears in a supply block, called when the supply is closed
=end code
Phasers marked with a C<*> have a run-time value, and if evaluated earlier than
their surrounding expression, they simply save their result for use in the
expression later when the rest of the expression is evaluated:
my $compiletime = BEGIN { now };
our $random = ENTER { rand };
As with other statement prefixes, these value-producing constructs may be
placed in front of either a block or a statement:
my $compiletime = BEGIN now;
our $random = ENTER rand;
Most of these phasers will take either a block or a function reference. The
statement form can be particularly useful to expose a lexically scoped
declaration to the surrounding lexical scope without "trapping" it inside a
block.
These declare the same variables with the same scope as the preceding example,
but run the statements as a whole at the indicated time:
BEGIN my $compiletime = now;
ENTER our $random = rand;
(Note, however, that the value of a variable calculated at compile time may not
persist under run-time cloning of any surrounding closure.)
Most of the non-value-producing phasers may also be so used:
END say my $accumulator;
Note, however, that
END say my $accumulator = 0;
sets the variable to 0 at C<END> time, since that is when the "my" declaration
is actually executed. Only argumentless phasers may use the statement form.
This means that C<CATCH> and C<CONTROL> always require a block, since they take
an argument that sets C<$_> to the current topic, so that the innards are able
to behave as a switch statement. (If bare statements were allowed, the
temporary binding of C<$_> would leak out past the end of the C<CATCH> or
C<CONTROL>, with unpredictable and quite possibly dire consequences. Exception
handlers are supposed to reduce uncertainty, not increase it.)
X<|Phasers, will trait>
Some of these phasers also have corresponding traits that can be set on
variables. These have the advantage of passing the variable in question into
the closure as its topic:
our $h will enter { .rememberit() } will undo { .forgetit() };
Only phasers that can occur multiple times within a block are eligible for this
per-variable form.
The topic of the block outside a phaser is still available as C<< OUTER::<$_>
>>. Whether the return value is modifiable may be a policy of the phaser in
question. In particular, the return value should not be modified within a
C<POST> phaser, but a C<LEAVE> phaser could be more liberal.
Any phaser defined in the lexical scope of a method is a closure that closes
over C<self> as well as normal lexicals. (Or equivalently, an implementation
may simply turn all such phasers into submethods whose primed invocant is the
current object.)
When multiple phasers are scheduled to run at the same moment, the general
tiebreaking principle is that initializing phasers execute in order declared,
while finalizing phasers execute in the opposite order, because setup and
teardown usually want to happen in the opposite order from each other.
=head2 Execution Order
Compilation Begins
=for code :skip-test
BEGIN {...} # at compile time, ASAP, only ever runs once
CHECK {...} # at compile time, ALAP, only ever runs once
COMPOSE {...} # when a role is composed into a class
Execution Begins
=for code :skip-test
INIT {...} # at run time, ASAP, only ever runs once
Before block execution begins
=for code :skip-test
PRE {...} # assert precondition at every block entry, before ENTER
Loop execution begins
=for code :skip-test
FIRST {...} # at loop initialization time, before any ENTER
Block execution begins
=for code :skip-test
ENTER {...} # at every block entry time, repeats on loop blocks.
Exception maybe happens
=for code :skip-test
CATCH {...} # catch exceptions, before LEAVE
CONTROL {...} # catch control exceptions, before LEAVE
End of loop, either continuing or finished
=for code :skip-test
NEXT {...} # at loop continuation time, before any LEAVE
LAST {...} # at loop termination time, after any LEAVE
End of block
=for code :skip-test
LEAVE {...} # at every block exit time (even stack unwinds from exceptions)
KEEP {...} # at every successful block exit, part of LEAVE queue
UNDO {...} # at every unsuccessful block exit, part of LEAVE queue
Postcondition for block
=for code :skip-test
POST {...} # assert postcondition at every block exit, after LEAVE
Async whenever-block is complete
=for code :skip-test
LAST {...} # if ended normally with done, runs once after block
QUIT {...} # catch async exceptions
Program terminating
=for code :skip-test
END {...} # at run time, ALAP, only ever runs once
=head1 Program Execution Phasers
=head2 X<BEGIN|Phasers, BEGIN>
Runs at compile time, as soon as possible, only runs once.
Can have a return value that is provided even in later phases.
=head2 X<CHECK|Phasers, CHECK>
Runs at compile time, As late as possible, only runs once.
Can have a return value that is provided even in later phases.
Code that is generated at run time can still fire off C<CHECK> and C<INIT>
phasers, though of course those phasers can't do things that would require
travel back in time. You need a wormhole for that.
=head2 X<INIT|Phasers, INIT>
Runs after compilation during main execution, as soon as possible, only runs
once.
Can have a return value that is provided even in later phases.
When phasers are in different modules, the C<INIT> and C<END> phasers are
treated as if declared at C<use> time in the using module. (It is erroneous to
depend on this order if the module is used more than once, however, since the
phasers are only installed the first time they're noticed.)
Code that is generated at run time can still fire off C<CHECK> and C<INIT>
phasers, though of course those phasers can't do things that would require
travel back in time. You need a wormhole for that.
An C<INIT> only runs once for all copies of a cloned closure.
=head2 X<END|Phasers, END>
Runs after compilation during main execution, as late as possible, only runs
once.
When phasers are in different modules, the C<INIT> and C<END> phasers are
treated as if declared at C<use> time in the using module. (It is erroneous to
depend on this order if the module is used more than once, however, since the
phasers are only installed the first time they're noticed.)
=head2 X<CLOSE|Phasers, CLOSE>
Appears in a supply block. Called when the supply is closed.
=head1 Block Phasers
Execution in the context of a block has its own phases.
Block-leaving phasers wait until the call stack is actually unwound to run.
Unwinding happens only after some exception handler decides to handle the
exception that way. That is, just because an exception is thrown past a stack
frame does not mean we have officially left the block yet, since the exception
might be resumable. In any case, exception handlers are specified to run within
the dynamic scope of the failing code, whether or not the exception is
resumable. The stack is unwound and the phasers are called only if an exception
is not resumed.
These can occur multiple times within the block. So they aren't really traits,
exactly--they add themselves onto a list stored in the actual trait. If you
examine the C<ENTER> trait of a block, you'll find that it's really a list of
phasers rather than a single phaser.
All of these phaser blocks can see any previously declared lexical variables,
even if those variables have not been elaborated yet when the closure is
invoked (in which case the variables evaluate to an undefined value.)
=head2 X<ENTER|Phasers, ENTER>
Runs at every block entry time, repeats on loop blocks.
Can have a return value that is provided even in later phases.
An exception thrown from an C<ENTER> phaser will abort the C<ENTER> queue, but
one thrown from a C<LEAVE> phaser will not.
=head2 X<LEAVE|Phasers, LEAVE>
Runs at every block exit time (even stack unwinds from exceptions),
except when the program exits abruptly (e.g. with
L«C<exit>|/routine/exit»).
C<LEAVE> phasers for a given block are necessarily evaluated after any
C<CATCH> and C<CONTROL> phasers. This includes the C<LEAVE> variants, C<KEEP>
and C<UNDO>. C<POST> phasers are evaluated after everything else, to guarantee
that even C<LEAVE> phasers can't violate postconditions.
An exception thrown from an C<ENTER> phaser will abort the C<ENTER> queue, but
one thrown from a C<LEAVE> phaser will not.
If a C<POST> fails or any kind of C<LEAVE> block throws an exception while the
stack is unwinding, the unwinding continues and collects exceptions to be
handled. When the unwinding is completed all new exceptions are thrown from
that point.
sub answer() {
LEAVE say „I say after the return value.“;
42 # this is the return value
}
B<Note:> be mindful of C<LEAVE> phasers directly in blocks of routines, as
they will get executed even when an attempt to call the routine with wrong
arguments is made:
sub foo (Int) {
say "Hello!";
LEAVE say "oh noes!"
}
try foo rand; # OUTPUT: «oh noes!»
Although the subroutine's body did not get run, because the sub expects
an L<Int> and L«C<rand>|/routine/rand» returned a L<Num>, its block was
entered and left (when param binding failed), and so the C<LEAVE> phaser
I<was> run.
=head2 X<KEEP|Phasers, KEEP>
Runs at every successful block exit, as part of the LEAVE queue (shares the
same order of execution).
For phasers such as C<KEEP> and C<POST> that are run when exiting a scope
normally, the return value (if any) from that scope is available as the current
topic within the phaser.
=head2 X<UNDO|Phasers, UNDO>
Runs at every unsuccessful block exit, as part of the LEAVE queue (shares the
same order of execution).
=head2 X<PRE|Phasers, PRE>
Asserts a precondition at every block entry. Runs before the ENTER phase.
C<PRE> phasers fire off before any C<ENTER> or C<FIRST>.
The exceptions thrown by failing C<PRE> and C<POST> phasers cannot be caught by
a C<CATCH> in the same block, which implies that C<POST> phaser are not run if
a C<PRE> phaser fails.
=head2 X<POST|Phasers, POST>
Asserts a postcondition at every block entry. Runs after the LEAVE phase.
For phasers such as C<KEEP> and C<POST> that are run when exiting a scope
normally, the return value (if any) from that scope is available as the current
topic within the phaser.
The C<POST> block can be defined in one of two ways. Either the corresponding
C<POST> is defined as a separate phaser, in which case C<PRE> and C<POST> share
no lexical scope. Alternately, any C<PRE> phaser may define its corresponding
C<POST> as an embedded phaser block that closes over the lexical scope of the
C<PRE>.
If a C<POST> fails or any kind of C<LEAVE> block throws an exception while the
stack is unwinding, the unwinding continues and collects exceptions to be
handled. When the unwinding is completed all new exceptions are thrown from
that point.
The exceptions thrown by failing C<PRE> and C<POST> phasers cannot be caught by
a C<CATCH> in the same block, which implies that C<POST> phaser are not run if
a C<PRE> phaser fails.
=head1 Loop Phasers
C<FIRST>, C<NEXT>, and C<LAST> are meaningful only within the lexical scope of
a loop, and may occur only at the top level of such a loop block.
=head2 X<FIRST|Phasers, FIRST>
Runs at loop initialization, before ENTER.
=head2 X<NEXT|Phasers, NEXT>
Runs when loop is continued (either through C<next> or because you got to the
bottom of the loop and are looping back around), before LEAVE.
A C<NEXT> executes only if the end of the loop block is reached normally, or an
explicit C<next> is executed. In distinction to C<LEAVE> phasers, a C<NEXT>
phaser is not executed if the loop block is exited via any exception other than
the control exception thrown by C<next>. In particular, a C<last> bypasses
evaluation of C<NEXT> phasers.
=head2 X<LAST|Phasers, LAST>
Runs when loop is aborted (either through C<last>, or C<return>, or because you
got to the bottom of the loop and are done), after LEAVE.
=head1 Exception Handling Phasers
=head2 X<CATCH|Phasers, CATCH>
Runs when an exception is raised by the current block, before the LEAVE phase.
=head2 X<CONTROL|Phasers, CONTROL>
Runs when a control exception is raised by the current block, before the LEAVE
phase. It is raised by C<return>, C<fail>, C<redo>, C<next>, C<last>, C<take>,
C<warn>, C<proceed> and C<succeed>.
=begin comment
# NYI
# =head1 Object Phasers
#
# =head2 X<COMPOSE|Phasers, COMPOSE>
#
# Runs when a role is composed into a class.
=end comment
=head1 Asynchronous Phasers
=head2 X<LAST|Asynchronous Phasers, LAST>
Runs when a L<Supply|/type/Supply> finishes with a call to C<done> or when a
C<supply> block exits normally. It runs completely after the C<whenever> block
it is placed within finishes.
This phaser reuses the name C<LAST>, but works differently from the C<LAST> loop
phaser. This phaser is similar to setting the C<done> routine while tapping a
supply with C<tap>.
=head2 X<QUIT|Asynchronous Phasers, QUIT>
Runs when a L<Supply|/type/Supply> terminates early with an exception. It runs
after the C<whenever> block it is placed within finishes.
This phaser is similar to setting the C<quit> routine while tapping a Supply
with C<tap>.
=head1 DOC phasers
=head2 X<DOC|Asynchronous Phasers, DOC>
The phasers C<BEGIN>, C<CHECK> and C<INIT> are run only in documentation mode when
prefixed with the C<DOC> keyword. The compiler is in documentation when run with C<--doc>.
DOC INIT { say 'init' } # prints 'init' at initialization time when in documentation mode.
=end pod
# vim: expandtab softtabstop=4 shiftwidth=4 ft=perl6