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
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
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
> 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/
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
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
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