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

Initializations outside of control flow

11 views
Skip to first unread message

Mark J. Reed

unread,
Apr 15, 2003, 10:34:35 AM4/15/03
to perl6-l...@perl.org
I have a question about, well, initializations outside of
control flow (bet you would never have guessed that from the
subject line!). Specifically, I'm considering the case where
shared variables need to be initialized before one of a set of
subroutines is invoked. This topic is related to that of the recent
Apocalypse/Synopsis pair, which is what got me thinking about it,
although the answer probably belongs properly in Apoc 11 or so.
However, it's entirely possible that this has actually been covered
already and I just didn't notice when I was skimming and looking
for it, in which case I apologize in advance for asking an
already-answered question.

Okay, end preamble. A common pattern for my smaller scripts is this:

configuration
main code
support code

(in larger scripts the support code mostly gets ripped out into
separate modules).

For a trivial example that I wouldn't actually write this way, take
a look at this (Perl5):

1. use strict;
2.
3. my $INITIAL_VALUE = 10;
4.
5. print join("\n", dec(), inc(), inc(), reset(), dec(), dec()), "\n";
6. exit(0);
7.
8. my $count = $INITIAL_VALUE
9. sub inc { return ++$count; }
10. sub dec { return --$count; }
11. sub reset { return $count = $INITIAL_VALUE; }

Now, clearly, this isn't going to have the intended result, because line
8 never gets executed. Therefore the initialization will never happen and
$count will be undef (and treated as 0) the first time dec() is called.

To fix this in Perl5, there are several options:

1. have each support sub check the shared value(s) and initialize
them if they haven't been initialized yet;

2. move the initialization(s) up above the main line of code;

3. move the support code into a module, which will get executed
in its entirety when use'd;

4. initialize the value(s) in a BEGIN block (of course, they still
have to be declared outside of that block to have the proper
scope).


None of these is particularly pleasant, however. With option 1 you end
up with a lot of nearly identical check-and-init code, plus you have
the usual limitations associated with sentinel values (what if undef is
a legal value of the variable(s) in question?). With option 2,
you have a textual mixture of levels, exposing internal implementation details
of the support code up before the main control flow. Option 3 is
fine in principle, but it's overkill for small programs.

Option 4 is probably the best of the bunch, but I find it aesthetically
unappealing (and if there's one thing I've learned from reading Larry Wall's
writings, it's to trust my aesthetic sense):

my $count; BEGIN { $count = $INITIAL_VALUE; }

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?

--
Mark REED | CNN Internet Technology
1 CNN Center Rm SW0831G | mark...@cnn.com
Atlanta, GA 30348 USA | +1 404 827 4754

Paul

unread,
Apr 15, 2003, 11:19:15 AM4/15/03
to Mark J. Reed, perl6-l...@perl.org
> my $count; BEGIN { $count = $INITIAL_VALUE; }

This won't work either; the BEGIN happens early, but the declaration
doesn't. You *could* do something like

BEGIN { our $count = $INITIAL_VALUE; }

though that isn't the same, because now it's not a lexical.

My usual solution is to say to myself that small scripts aren't a big
deal, and just do all the inits in a block at the top, and think of it
as pseudo-documentation support. :)

Moreso, I abstract out things I do often into modules that my little
scripts use, but I realize that may not do what you want. Still, if you
do this often enough it might be worth it to you to create a small
module that encapsulates the desired result.

# myVar eval's them to lexicals and handles them with accessors
use myVar qw/ $INITIAL_VALUE=10 $count=$INITIAL_VALUE /;
# . . .
sub inc { ${myVar->count}++ }

Not much better, but it's a start,maybe?


__________________________________________________
Do you Yahoo!?
The New Yahoo! Search - Faster. Easier. Bingo
http://search.yahoo.com

Luke Palmer

unread,
Apr 15, 2003, 11:25:09 AM4/15/03
to Hod...@writeme.com, mark...@turner.com, perl6-l...@perl.org
> > my $count; BEGIN { $count = $INITIAL_VALUE; }
>
> This won't work either; the BEGIN happens early, but the declaration
> doesn't. You *could* do something like
>
> BEGIN { our $count = $INITIAL_VALUE; }
>
> though that isn't the same, because now it's not a lexical.

It does too work. The declaration is a compile-time thing.

perl -le 'foo(); bar(); { my $x; BEGIN { $x = 16; } sub foo { print $x
} } sub bar { print $x }'

Prints <<EOO;
16

EOO

Luke

Mark J. Reed

unread,
Apr 15, 2003, 11:57:53 AM4/15/03
to perl6-l...@perl.org

On 2003-04-15 at 08:19:15, Paul wrote:
> > my $count; BEGIN { $count = $INITIAL_VALUE; }
>
> This won't work either;
Ah, but it does - give it a try! I make a habit of testing code
snippets (my own or someone else's) before sending email to the list about
them. Or at least, I do that with code which I have the ability to run;
that generally excludes Perl6 code, unfortunately.

> the BEGIN happens early, but the declaration doesn't.

Nope. Lexical declarations are processed at compile time, even though
any initialization doesn't happen until run time. A line like
"my $count = $INITIAL_VALUE;" is effectively processed twice:
first, during compilation, the compiler creates the variable for the benefit
of the following code in the containing scope; then, when (if) the
runtime execution path reaches the line, the variable is
initialized.

Paul

unread,
Apr 15, 2003, 12:36:02 PM4/15/03
to Mark J. Reed, perl6-l...@perl.org

--- "Mark J. Reed" <mark...@turner.com> wrote:
> On 2003-04-15 at 08:19:15, Paul wrote:
> > > my $count; BEGIN { $count = $INITIAL_VALUE; }
> >
> > This won't work either;
>
> Ah, but it does - give it a try! I make a habit of testing code
> snippets (my own or someone else's) before sending email to the
> list about them. Or at least, I do that with code which I have
> the ability to run; that generally excludes Perl6 code,
> unfortunately.
>
> > the BEGIN happens early, but the declaration doesn't.
>
> Nope. Lexical declarations are processed at compile time, even
> though any initialization doesn't happen until run time. A line
> like "my $count = $INITIAL_VALUE;" is effectively processed twice:
> first, during compilation, the compiler creates the variable for the
> benefit of the following code in the containing scope; then, when
> (if) the runtime execution path reaches the line, the variable is
> initialized.

My mistake. I've been bitten by something like this before, and
obviously got the two confused.

Now I'm wondering what the hell it was that got me last time,
though....
>:o/

Larry Wall

unread,
Apr 15, 2003, 2:19:42 PM4/15/03
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

Arcadi Shehter

unread,
Apr 15, 2003, 4:38:28 PM4/15/03
to Larry Wall, perl6-l...@perl.org

Larry Wall writes:
>
> state $where is begin($x);
> state $where is check($x);
> state $where is init($x);
> state $where is first($x);
>

may be not directly related to this thread , but still ..:

is it correct that first three lines will force the closure to close
only once

-or-

in analogy with discussion of "=" , the value of the closure property
will be calculated at the moment prescribed by begin/check/init
and every time closure is created the property $where will be set to
the (pre)calculated value

arcadi

Larry Wall

unread,
Apr 16, 2003, 4:44:13 PM4/16/03
to perl6-l...@perl.org
On Tue, Apr 15, 2003 at 10:38:28PM +0200, arcadi shehter wrote:
:

The latter, if I understand your question right. The instantiation of a
closure recreates any state variable. Only "first" is guaranteed to
happen after that. And only "begin" is guaranteed to happen before!
I wonder what "check" and "init" should do when called at compile time.

Larry

0 new messages