Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Magic blocks (was: Compile-time undefined sub detection)

14 views
Skip to first unread message

Larry Wall

unread,
Mar 6, 2004, 9:39:44 PM3/6/04
to perl6-l...@perl.org
Okay you guys, you're running away from one kind of madness and
proposing various other kinds of madness in its place. Mostly you're
confusing what should be easy and what should be possible.

First a note about cleanup dependencies. In general, these should be
driven by the structure of the data, not the structure of the program.
If you want to make sure one piece of data is cleaned up before
another, make sure there's a reference that enforces that.

That's the principle on cleanup. It's also, more or less, the
principle on initialization. What these various blocks are obscuring
from your calculations is that there is a data structure for each of
these sets of queued events, and as long as there's a way to name such
a queue, it's not necessary to access it through the official "block"
name. It would be nice if the property that stores a particular
queue has a related name, of course. But for fancy things, we don't
have to add options to the syntax of blocks in order to manipulate
these queues. If you want to make sure you're last in the END queue,
just manipulate that queue.

We also don't need a new abstraction like "execution group", unless by
that you simply mean one of these callback queues. We particularly
don't need to orthogonalize all the names and make them equally
obfuscational. This is very much a place where easy things should
be easy, and hard things should be difficult.

So here's the ruling. We don't need any special names for hacking
on the queue in another module. So CHECK means UNITCHECK, always.
There's no MAINCHECK name. If you want to do something fancy to
the main program's CHECK list or END list, then at export time,
modify the queue contained in Main's CHECKLIST property or ENDLIST
property. If you really, really, really, want something to run last,
then install something into Main::ENDLIST that looks to see whether
there's anything after it when it gets triggered, and if so, reinstalls
itself at the end again. And then we can have the entertaining
situation of two or more modules both playing leapfrog trying to
be last. Perhaps something fancy with priorities can be worked out,
but if so, that's the domain of direct queue manipulation routines.
It has nothing to do with the easy-to-use block declarations, which
have to remain simple interfaces, or there's no point to them.

So we're back to the standard four phase oriented blocks:

my @x will begin {...} # at BEGIN time
my @x will check {...} # at CHECK time (redefined to unit check)
my @x will init {...} # at INIT time
my @x will end {...} # at END time

plus these four block oriented blocks:

my @x will pre {...} # at PRE time (return boolean for DBC)
my @x will enter {...} # at ENTER time (block entry)
my @x will leave {...} # at LEAVE time (block exit)
my @x will post {...} # at POST time (return boolean for DBC)

plus three specialty times for things with lifetimes not related to
blocks:

state @x will first {...} # at FIRST time (first time through)
has @.x will build {...} # at BUILD time (object init time)
has @.x will destroy {...} # at DESTROY time (object final time)

There's no LAST corresponding to FIRST because you can't generally
tell when you've called a block for the very last time. (If there
is a LAST, it'd be loop related, but then FIRST and LAST aren't
really opposites, and it'd duplicate LEAVE anyway.)

Oh, and I guess we still have the two "leave" variants:

my @x will keep {...} # at LEAVE time, if leaving successfully
my @x will undo {...} # at LEAVE time, if leaving unsuccessfully

There's also, in a sense, a "catch" time, but you can't have more than
one of those, so there's no CATCHLIST property to modify. If you're
trying to add semantics to your CATCH, you're probably wanting an
"undo" instead.

A good case can be made for all of those--unless you happen to be
against transactional programming, or design by contract. Or state
variables, or objects, or blocks, or modules... :-)

Larry

Larry Wall

unread,
Mar 6, 2004, 10:39:58 PM3/6/04
to perl6-l...@perl.org
Oh, I accidentally left NEXT out of my canonical list. We do need to
have the equivalent to Perl 5's "continue".

Larry

Dave Mitchell

unread,
Mar 8, 2004, 6:57:04 PM3/8/04
to perl6-l...@perl.org
On Sat, Mar 06, 2004 at 06:39:44PM -0800, Larry Wall wrote:
> my @x will begin {...} # at BEGIN time
> my @x will check {...} # at CHECK time (redefined to unit check)
> my @x will init {...} # at INIT time
> my @x will end {...} # at END time

Sorry, perhaps I wasn't paying close enough attention, but suddenly we've
leaped from oddly named subs that get called at interesting times, to
array variables with oddly-named properties (or attributes, or whatever).
Je ne comprend pas :-(.

Dave.

--
A walk of a thousand miles begins with a single step...
then continues for another 1,999,999 or so.

Larry Wall

unread,
Mar 8, 2004, 7:23:51 PM3/8/04
to perl6-l...@perl.org
On Mon, Mar 08, 2004 at 11:57:04PM +0000, Dave Mitchell wrote:

: On Sat, Mar 06, 2004 at 06:39:44PM -0800, Larry Wall wrote:
: > my @x will begin {...} # at BEGIN time
: > my @x will check {...} # at CHECK time (redefined to unit check)
: > my @x will init {...} # at INIT time
: > my @x will end {...} # at END time
:
: Sorry, perhaps I wasn't paying close enough attention, but suddenly we've
: leaped from oddly named subs that get called at interesting times, to
: array variables with oddly-named properties (or attributes, or whatever).
: Je ne comprend pas :-(.

Oddly named subs are cool as such, but there are times when you
don't so much want an oddly named sub as an oddly named method.
These are just oddly named methods on the container in question
(which certainly doesn't have to be an array). Underneath they're
still oddly named subs in terms of when they run. But they implicitly
get (as their first argument/invocant/topic) the container in question,
so they can do fancy things with $_ and .foo and C<when> and such.

The "will first" syntax is mentioned in A6. Plus we discussed it
last April with respect to "state" variables--see message below.

Larry

From perl6-all-return-38843-larry=wall...@perl.org Tue Apr 15 11:56:23 2003
Date: Tue, 15 Apr 2003 11:19:42 -0700
From: Larry Wall <la...@wall.org>
Subject: Re: Initializations outside of control flow
To: perl6-l...@perl.org

On Tue, Apr 15, 2003 at 10:34:35AM -0400, Mark J. Reed wrote:
: It just seems like there should be a way to accomplish that without
: the visual clutter. Perhaps a trait:
:
: my $count is initially($INITIAL_VALUE);
:
: If there's already a defined way of doing that in Perl 6, please
: point me at it and I'll go away chastened but happy. Otherwise,
: does this seem a reasonable approach? Ideas for a better name for
: the trait?

The traits in question are already named corresponding to the
various initialization closures, which at the moment are named

BEGIN {...}
CHECK {...}
INIT {...}
FIRST {...}
ENTER {...}

so, since the variable in question is the topic of the closure, we have

my $count will begin { .set($INITIAL_VALUE) }
my $count will check { .set($INITIAL_VALUE) }
my $count will init { .set($INITIAL_VALUE) }
my $count will first { .set($INITIAL_VALUE) }
my $count will enter { .set($INITIAL_VALUE) }

Perhaps those particular traits are smart enough to distinguish whether
they have a closure or not, and build an appropriate closure if not,
so we could also have

my $count is begin($INITIAL_VALUE)
my $count is check($INITIAL_VALUE)
my $count is init($INITIAL_VALUE)
my $count is first($INITIAL_VALUE)
my $count is enter($INITIAL_VALUE)

At the moment, I believe that = always happens at run-time. So

my $count = BEGIN { calculation() }

would do the calculation at compile time but assign the resulting
value every time the statement is executed. This means that a
declaration like:

state $where = $x;

is almost certainly incorrect. It should probably be one of

state $where is begin($x);
state $where is check($x);
state $where is init($x);
state $where is first($x);

Could go so far as to lose the "is", treating these as variants of
"is".

state $where begin($x);
state $where check($x);
state $where init($x);
state $where first($x);

The other possible approach would be to allow blockless closures like

BEGIN (state $where = $x);
CHECK (state $where = $x);
INIT (state $where = $x);
FIRST (state $where = $x);

Maybe the parens aren't necessary, if they just introduce a syntactic
statement:

BEGIN state $where = $x;
CHECK state $where = $x;
INIT state $where = $x;
FIRST state $where = $x;

But I think it would be a mistake to have = running at random times
depending on either the "foo" and "BAR" in

foo $x = BAR { ... }

Larry

Joe Gottman

unread,
Mar 9, 2004, 10:03:30 PM3/9/04
to perl6-l...@perl.org


Will there be a corresponding 'will next' property on variables? If so,
then this could be a very nice way to solve the problem of how to access the
'prior' element in a for loop:

for @array -> $current
{
state $prior will next {$prior := $current} will leave {$prior :=
undef};
...
}

$prior is a state variable so that it retains its value between
iterations of the loop. The 'will next' property binds $prior to $current
just before the loop binds $current to the next element of @array; the
result is that in every iteration of the loop after the first $prior is
bound to the same element of @array that $current was bound to in the
previous iteration. The 'will leave' property ensures that we don't keep a
reference to an element of @array after we leave the loop; it also ensures
that $prior has the correct value of undef() when we start the loop the next
time.

Joe Gottman


0 new messages