develooper Front page | perl.perl5.porters | Postings from July 2009

Re: %^H (was Re: Coring Variable::Magic / autodie fights with string eval in Perl 5.10.x)

Thread Previous | Thread Next
Rafael Garcia-Suarez
July 7, 2009 09:19
Re: %^H (was Re: Coring Variable::Magic / autodie fights with string eval in Perl 5.10.x)
Message ID:
2009/7/7 Abigail <>:
>> How does P6 do it ? Isn't there a special block à la BEGIN/END/CHECK etc ?
>> (B::Hooks::EndOfScope uses a special block syntax)
> Yes, it's LEAVE (any block exit), KEEP (succesful exit), UNDO (unsuccesful).

There are many features that would be nice for Perl 5 in that list.
But nothing to install a special block in the caller at compile-time.

> Quoting from
> =head1 Closure traits
> A C<CATCH> block is just a trait of the closure containing it.  Other
> blocks can be installed as traits as well.  These other blocks are
> called at various times, and some of them respond to various control
> exceptions and exit values:
>      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
>      START {...}*      on first ever execution, once per closure clone
>      ENTER {...}*      at every block entry time, repeats on loop blocks.
>      LEAVE {...}       at every block exit time
>       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
> Those marked with a C<*> can also be used within an expression:
>    my $compiletime = BEGIN { localtime };
>    our $temphandle = START { maketemp() };
> As with other statement prefixes, these value-producing constructs
> may be placed in front of either a block or a statement:
>    my $compiletime = BEGIN localtime;
>    our $temphandle = START maketemp();
> This can be particularly useful to expose a lexically scoped
> declaration to the surrounding context.  Hence 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 = localtime;
>    START our $temphandle = maketemp();
> (Note, however, that the value of a variable calculated at compile
> time may not persist under run-time cloning of any surrounding closure.)
> Code that is generated at run time can still fire off C<CHECK>
> and C<INIT> blocks, though of course those blocks can't do things that
> would require travel back in time.
> Some of these 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:
>    my $r will start { .set_random_seed() };
>    our $h will enter { .rememberit() } will undo { .forgetit() };
> Apart from C<CATCH> and C<CONTROL>, which can only occur once, most
> of 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 (except for C<START>, which executes inline).  So if you
> examine the C<ENTER> trait of a block, you'll find that it's really
> a list of closures rather than a single closure.
> The semantics of C<INIT> and C<START> are not equivalent to each
> other in the case of cloned closures.  An C<INIT> only runs once for
> all copies of a cloned closure.  A C<START> runs separately for each
> clone, so separate clones can keep separate state variables:
>    our $i = 0;
>    ...
>    $func = { state $x will start { $x = $i++ }; dostuff($i) };
> But C<state> automatically applies "start" semantics to any initializer,
> so this also works:
>    $func = { state $x = $i++; dostuff($i) }
> Each subsequent clone gets an initial state that is one higher than the
> previous, and each clone maintains its own state of C<$x>, because that's
> what C<state> variables do.
> Even in the absence of closure cloning, C<INIT> runs before the
> mainline code, while C<START> puts off the initialization till the
> last possible moment, then runs exactly once, and caches its value
> for all subsequent calls (assuming it wasn't called in void context,
> in which case the C<START> is evaluated once only for its side effects).
> In particular, this means that C<START> can make use of any parameters
> passed in on the first call, whereas C<INIT> cannot.
> All of these trait 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.)
> Note: Apocalypse 4 confused the notions of C<PRE>/C<POST> with C<ENTER>/C<LEAVE>.
> These are now separate notions.  C<ENTER> and C<LEAVE> are used only for
> their side effects.  C<PRE> and C<POST> must return boolean values that are
> evaluated according to the usual Design by Contract (DBC) rules.  (Plus,
> if you use C<ENTER>/C<LEAVE> in a class block, they only execute when the
> class block is executed, but C<PRE>/C<POST> in a class block are evaluated
> around every method in the class.)  C<KEEP> and C<UNDO> are just variants
> of C<LEAVE>, and for execution order are treated as part of the queue of
> C<LEAVE> blocks.
> 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.  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> blocks, a C<NEXT> block 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>
> blocks.
> [Note: the name C<FIRST> used to be associated with C<state>
> declarations.  Now it is associated only with loops.  See the C<START>
> above for C<state> semantics.]
> C<LEAVE> blocks are evaluated after C<CATCH> and C<CONTROL> blocks, including
> the C<LEAVE> variants, C<KEEP> and C<UNDO>.  C<POST> blocks are evaluated after
> everything else, to guarantee that even C<LEAVE> blocks can't violate DBC.
> Likewise C<PRE> blocks fire off before any C<ENTER> or C<FIRST> (though not
> before C<BEGIN>, C<CHECK>, or C<INIT>, since those are done at compile or
> process initialization time).
> For blocks 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.  (It is presented as a C<Capture> object.)
> The topic of the outer block is still available as C<< OUTER::<$_> >>.
> Whether the return value is modifiable may be a policy of the block
> in question.  In particular, the return value should not be modified
> within a C<POST> block, but a C<LEAVE> block could be more liberal.

"You don't mean odds and ends, you mean des curieux et des bouts",
corrected the manager.
-- Terry Pratchett, Hogfather

Thread Previous | Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About