=head1 TITLE
Exegesis 4: Syntax
=head1 AUTHOR
Damian Conway <damian
@conway
.org>
=head1 VERSION
Maintainer: Allison Randal <al
@shadowed
.net>
Date: 2 Apr 2002
Last Modified: unknown
Number: 4
Version: 1
=over
=item B<I<And I'd se-ell
my
-y so-oul
for
flow of con-tro-ol ... over Perl>>
=over
=item B<-- The Motels,
"Total Control"
(Perl 6 remix)>
=back
=back
In Apocalypse 4, Larry explains the fundamental changes to flow and
block control in Perl 6. The changes bring fully integrated exceptions;
a powerful new switch statement; a coherent mechanism
for
polymorphic
matching; a greatly enhanced C<
for
> loop; and unification of blocks,
subroutines and closures.
Let's dive right in.
=head1 "Now, Witness the Power of This Fully I<Operational> Control
Structure"
We'll consider a simple interactive
thing would have many more operators and
values
, but that's not
important right now.
class Err::BadData is Exception {...}
module Calc;
my
class NoData is Exception {
method
warn
(*
@args
) {
die
@args
}
}
my
%var
;
my
sub
get_data (
$data
) {
given
$data
{
when
/^\d+$/ {
return
%var
{
""
} =
$_
}
when
'previous'
{
return
%var
{
""
} // fail NoData }
when
%var
{
return
%var
{
""
} =
%var
{
$_
} }
default
{
die
Err::BadData :
msg
=>
"Don't understand $_"
}
}
}
sub
calc (str
$expr
,
int
$i
) {
our
%operator
is private //= (
'*'
=> { $^a * $^b },
'/'
=> { $^a / $^b },
'~'
=> { ($^a + $^b) / 2 },
);
my
@stack
;
my
$toknum
= 1;
for
split
/\s+/,
$expr
->
$token
{
try
{
when
%operator
{
my
@args
=
splice
@stack
, -2;
push
@stack
,
%operator
{
$token
}(*
@args
)
}
when
'.'
,
';'
,
'='
{
last
}
push
@stack
, get_data(
$token
);
CATCH {
when
Err::Reportable {
warn
$!;
continue
}
when
Err::BadData { $!.fail(
at
=>
$toknum
) }
when
NoData {
push
@stack
, 0 }
when
/division by zero/ {
push
@stack
, Inf }
}
}
NEXT {
$toknum
++ }
}
fail Err::BadData:
msg
=>
"Too many operands"
if
@stack
> 1;
return
%var
{
'$'
_
$i
} =
pop
(
@stack
) but true;
}
module main;
for
1..Inf ->
$i
{
print
"$i> "
;
my
$expr
= <> err
last
;
print
"$i> $( Calc::calc(i=>$i, expr=>$expr) )\n"
;
}
=head1 An Exceptionally Promising Beginning
The calculator is going to handle internal and external errors using
Perl 6
's OO exception mechanism. This means that we'
re going to need
some classes
for
those OO exceptions to belong to.
To create those classes, the C<class> keyword is used. For example:
class Err::BadData is Exception {...}
After this declaration, C<Err::BadData> is a class name (or rather, by
analogy to
"filehandle,"
it's a
"classname"
). Either way, it can then
be used as a type specifier wherever Perl 6 expects one. Unlike Perl 5,
that classname is not a bareword string: It's a genuine first-class
symbol in the program. In object-oriented terms, we could think of a
classname as a meta-object -- an object that describes the attributes
and behavior of other objects.
Modules and packages are also first class in Perl 6, so we can also
refer to their names directly, or take references to them, or look them
up in the appropriate symbol table.
Classes can take properties, just like variables and
values
. Generally,
those properties will specify variations in the behavior of the class.
For example:
class B::Like::Me is interface;
specifies that the B::Like::Me class defines a (Java-like) interface
that any subclass must implement.
The C<is Exception> is not, however, a standard property. Indeed,
C<Exception> is the name of another (standard, built-in) class. When a
classname like this is used as
if
it were a property, the property it
confers is inheritance. Specifically, C<Err::BadData> is
defined
as
inheriting from the C<Exception> base class. In Perl 5, that would have
been:
So now class C<Err::BadData> will have all the exceptionally useful
properties of the C<Exception> class.
Having classnames as
"first class"
symbols of the program means that
it's also important to be able to pre-declare them (to avoid
compile-
time
"no such class or module"
errors). So we need a new syntax
for
declaring the existence of classes/modules/packages, without
actually defining their behavior.
To
do
that we
write
:
class MyClass {...}
That right. That's real, executable, Perl 6 code.
We're defining the class, but using the new Perl 6
"yada-yada-yada"
operator in a block immediately
after
the classname. By using the
"I'm-eventually-going-to-put-something-here-but-not-just-yet"
marker,
we indicate that this definition is only a stub or placeholder. In this
way, we introduce the classname into the current scope without needing
to provide the complete description of the class.
By the way, this is also the way we can declare other types of symbols
in Perl 6 without actually defining them:
module Alpha {...}
method Gamma::delta(Gamma
$self
:
$d1
,
$d2
) {...}
sub
epsilon() {...}
In
our
example, the C<Err::BadData> classname is introduced in
precisely that way:
class Err::BadData is Exception {...}
which means that we can refer to the class by name, even though it
has
not yet been completely
defined
.
In fact, in this example, C<Err::BadData> is I<never> completely
defined
. So we'd get a fatal compile-
time
error: "Missing definition
for
class Err::BadData." Then we'd realize we either forgot to
eventually define the class, or that we had really meant to
write
:
class Err::BadData is Exception {}
=head1 Lexical Exceptions
Most of the implementation of the calculator is contained in the
C<Calc> module. In Perl 6, modules are specified using the C<module>
keyword:
module Calc;
which is similar in effect to a Perl 5:
Modules are not quite the same as packages in Perl 6. Most
significantly, they have a different export mechanism: They export via
a new, built-in, declarative mechanism (which will be described in a
future Apocalypse) and the symbols they export are exported lexically
by
default
.
The first thing to appear in the module is a class declaration:
my
class NoData is Exception {
method
warn
(*
@args
) {
die
@args
}
}
This is another class derived from C<Exception>, but one that
has
two
significant differences from the declaration of C<class Err::BadData>:
=over
=item *
The leading C<
my
> makes it lexical in scope, and
=item *
the trailing braces give it an associated block in which its
attributes and methods can be specified.
=back
Let's look at
each
of those.
C<NoData> exceptions are only going to be used within the C<Calc>
module itself. So it's good software engineering to make them visible
only within the module itself.
Why? Because
if
we ever attempt to refer to the exception class outside
C<Calc> (e.g.
if
we tried to
catch
such an exception in C<main>), then
we'll get a compile-
time
"No such class: NoData"
error. Any such errors
would indicate a flaw in
our
class design or implementation.
In Perl 6, classes are first-class constructs. That is, like variables
and subroutines, they are
"tangible"
components of a program, denizens
of a symbol table, able to be referred to both symbolically and by
explicit reference:
$class
= \Some::Previously::Defined::Class;
$obj
=
$class
.new();
Note that the back slash is actually optional in that first line, just
as it would be
for
an array or hash in the same position.
"First class"
also means that classnames live in a symbol table. So it
follows that they can be
defined
to live in the current I<lexical>
symbol table (i.e. C<
%MY::
>), by placing a C<
my
>
before
them.
A lexical class or module is only accessible in the lexical scope in
which it's declared. Of course, like Perl 5 packages, Perl 6 classes
and modules don't usually I<have> an explicit lexical scope associated
with
their declaration. They are implicitly associated
with
the
surrounding lexical scope (which is normally a file scope).
But we can give them their own lexical scope to preside over by adding
a block at the end of their declaration:
class Whatever {
}
This turns out to be important. Without the ability to specify a
lexical scope over which the class
has
effect, we would be stuck
with
no
way to embed a
"nested"
lexical class:
class Outer;
my
class Inner;
In Perl 6, we avoid this problem by writing:
class Outer;
my
class Inner {
}
In
our
example, we
use
this new feature to redefine C<NoData>'s C<
warn
>
method (upgrading it to a call to C<
die
>). Of course, we could also
have done that
with
just:
my
class NoData is Exception;
method
warn
(*
@args
) {
die
@args
}
but then we would have needed to
"reopen"
the C<Calc> module's
namespace afterward:
module Calc;
my
class NoData is Exception;
method
warn
(*
@args
) {
die
@args
}
module Calc;
Being able to
"nest"
the C<NoData> namespace:
module Calc;
my
class NoData is Exception {
method
warn
(*
@args
) {
die
@args
}
}
is much cleaner.
By the way, because classes can now have an associated block, they can
even be anonymous:
$anon_class
= class {
};
$obj
=
$anon_class
.new();
which is a handy way of implementing
"singleton"
objects:
my
$allocator
= class {
my
$.count =
"ID_000001"
;
method next_ID { $.count++ }
}.new;
for
@objects
{
$_
.set_id(
$allocator
.next_ID );
}
=head1 Maintaining Your State
To store the
values
of any variables used by the calculator, we'll
use
a single hash,
with
each
key being a variable name:
my
%var
;
Nothing more to see here. Let's move along.
=head1 It's a Given
The C<get_data> subroutine may be
given
a number (i.e. a literal
value), a numerical variable name (i.e. C<
'$1'
>, C<
'$2'
>, etc.) , or
the keyword C<
'previous'
>.
It then looks up the information in the C<
%var
> hash, using a switch
statement to determine the appropriate look-up:
my
sub
get_data (
$data
) {
given
$data
{
The C<
given
$data
> evaluates its first argument (in this case,
C<
$data
>) in a
scalar
context, and makes the result the
"topic"
of
each
subsequent C<
when
> inside the block associated
with
the C<
given
>.
(Though, just between us, that block is merely an anonymous closure
acting as the C<
given
>'s second argument -- in Perl 6 I<all> blocks are
merely closures that are slumming it.)
Note that the C<
given
$data
> statement also makes C<
$_
> an alias
for
C<
$data
>. So,
for
example,
if
the C<
when
> specifies a pattern:
when
/^\d+$/ {
return
%var
{
""
} =
$_
}
then that pattern is matched against the contents of C<
$data
> (i.e.
against the current topic). Likewise, caching and returning C<
$_
>
when
the pattern matches is the same as caching and returning C<
$data
>.
After a C<
when
>'s block
has
been selected and executed, control
automatically passes to the end of the surrounding C<
given
> (or, more
generally, to the end of whatever block provided the C<
when
>'s topic).
That means that C<
when
> blocks don't
"fall through"
in the way that
C<case> statements
do
in C.
You can also explicitly
send
control to the end of a C<
when
>'s
surrounding C<
given
>, using a C<break> statement. For example:
given
$number
{
when
/[02468]$/ {
if
(
$_
== 2) {
warn
"$_ is even and prime\n"
;
break;
}
warn
"$_ is even and composite\n"
;
}
when
&is_prime
{
warn
"$_ is odd and prime\n"
;
}
warn
"$_ is odd and composite\n"
;
}
Alternatively, you can explicitly
tell
Perl not to automatically
C<break> at the end of the C<
when
> block. That is,
tell
it to "fall
through" to the statement immediately
after
the C<
when
>. That's done
with
a C<
continue
> statement (which is the new name
for
The Statement
Formerly Known As C<skip>):
given
$number
{
when
&is_prime
{
warn
"$_ is prime\n"
;
continue
; }
when
/[13579]$/ {
warn
"$_ is odd"
; }
when
/[02468]$/ {
warn
"$_ is even"
; }
}
In Perl 6, a C<
continue
> means: "
continue
executing from the
next
statement
after
the current C<
when
>, rather than jumping out of the
surrounding C<
given
>." It
has
nothing to
do
with
the old Perl 5
C<
continue
> block, which in Perl 6 becomes C<NEXT>.
The
"topic"
that C<
given
> creates can also be aliased to a name of
our
own choosing (though it's I<always> aliased to C<
$_
>
no
matter what
else
we may
do
). To give the topic a more meaningful name, we just need
to
use
the
"topical arrow:"
given
check_online().{active}{names}[0] ->
$name
{
when
/^\w+$/ {
print
"$name's on first\n"
}
when
/\?\?\?/ {
print
"Who's on first\n"
}
}
Having been replaced by the dot, the old Perl 5 arrow operator is
given
a new role in Perl 6. When placed
after
the topic specifier of a
control structure (i.e. the
scalar
argument of a C<
given
>, or the list
of a C<
for
>), it allows us to give an extra name (apart from C<
$_
>) to
the topic associated
with
that control structure.
In the above version, the C<
given
> statement declares a lexical
variable C<
$name
> and makes it yet another way of referring to the
current topic. That is, it aliases both C<
$name
> and C<
$_
> to the value
specified by C<check_online().{active}{names}[0]>.
This is a fundamental change from Perl 5, where C<
$_
> was only aliased
to the current topic in a C<
for
> loop. In Perl 6, the current topic --
whatever its name and however you make it the topic -- is I<always>
aliased to C<
$_
>.
That implies that everywhere that Perl 5 used C<
$_
> as a
default
(i.e.
C<
print
>, C<
chomp
>, C<
split
>, C<
length
>, C<
eval
>, etc.), Perl 6 uses
the current topic:
for
@list
->
$next
{
print
if
length
> 10;
%count
{
$next
}++;
}
This is subtly different from the
"equivalent"
Perl 5 code:
for
my
$next
(
@list
) {
print
if
length
> 10;
%count
{
$next
}++;
}
If you had wanted this Perl 5 behavior in Perl 6, then you'd have to
say
explicitly what you meant:
my
$outer_underscore
:=
$_
;
for
@list
->
$next
{
print
$outer_underscore
if
length
$outer_underscore
> 10;
%count
{
$next
}++;
}
which is probably a good thing in code that subtle.
Oh, and yes: the C<p52p6> translator program I<will> take that new
behavior into account and correctly convert something pathological
like:
while
(<>) {
for
my
$elem
(
@list
) {
print
if
$elem
% 2;
}
}
to:
for
<> {
my
$some_magic_temporary_variable
:=
$_
;
for
@list
->
$elem
{
print
$some_magic_temporary_variable
if
$elem
% 2;
}
}
Note that this works because, in Perl 6, a call to C<< <> >> is
lazily evaluated in list contexts, including the list of a C<
for
> loop.
=head1 Other whens
The remaining cases of the data look-up are handled by subsequent
C<
when
> statements. The first:
when
'previous'
{
return
%var
{
""
} // fail NoData }
handles the special keyword C<
"previous"
>. The previous value is always
stored in the element of C<
%var
> whose key is the empty string.
If, however, that previous value is undefined, then the defaulting
operator -- C<//> -- causes the right-hand side of the expression to be
evaluated instead. That right-hand side is a call to the C<fail> method
of class C<NoData> (and could equally have been written
C<NoData.fail()>).
The standard C<fail> method inherited from the C<Exception> class
constructs an instance of the appropriate class (i.e. an exception
object) and then either throws that exception (
if
the C<
use
fatal>
pragma is in effect) or
else
returns an C<
undef
> value from the scope
in which the C<fail> was invoked. That is, the C<fail> acts like a
C<
die
SomeExceptionClass> or a C<
return
undef
>, depending on the state
of the C<
use
fatal> pragma.
This is possible because, in Perl 6, I<all> flow-of-control --
including the normal subroutine C<
return
> -- is exception-based. So,
when
it is supposed to act like a C<
return
>, the C<Exception::fail>
method simply throws the special C<Ctl::Return> exception, which
C<get_data>'s
caller
will (automagically)
catch
and treat as a normal
return
.
So then why not just
write
the usual:
return
undef
;
instead?
The advantage of using C<fail> is that it allows the I<callers> of
C<get_data> to decide how that subroutine should signal failure. As
explained above, normally C<fail> fails by returning C<
undef
>. But
if
a
C<
use
fatal> pragma is in effect, any invocation of C<fail> instead
throws the corresponding exception.
What's the advantage in that? Well, some people feel that certain types
of failures ought to be taken deadly seriously (i.e. they should
kill
you
unless
you explicitly
catch
and handle them). Others feel that the
same errors really aren't all that serious and you should be allowed
to, like, chill man and just groove
with
the heavy consequences, dude.
The C<fail> method allows you, the coder, to stay well out of that kind
of fruitless religious debate.
When you
use
C<fail> to signal failure, not only is the code nicely
documented at that point, but the mode of failure becomes
caller
-selectable. Fanatics can C<
use
fatal> and make
each
failure
punishable by death; hippies can
say
C<
no
fatal> and make
each
failure
just
return
C<
undef
>.
You
no
longer have to get caught up in endless debate as to whether the
exception-catching:
try
{
$data
= get_data(
$str
) }
//
warn
"Couldn't get data"
}
is inherently better or worse than the C<
undef
>-sensing:
do
{
$data
= get_data(
$str
) }
//
warn
"Couldn't get data"
;
Instead, you can just
write
C<get_data> such that There's More Than One
Way To Fail It.
By the way, C<fail> can fail in other ways, too: in different contexts
or under different pragmas. The most obvious example would be inside a
regex, where it would initiate back-tracking. More on that in
Apocalypse 5.
=head1 Still Other Whens
Meanwhile,
if
C<
$data
> isn't a number or the C<
"previous"
> keyword,
then maybe it
's the name of one of the calculator'
s variables. The
third C<
when
> statement of the switch tests
for
that:
when
%var
{
return
%var
{
""
} =
%var
{
$_
} }
If a C<
when
> is
given
a hash, then it uses the current topic as a key
in the hash and looks up the corresponding entry. If that value is
true, then it executes its block. In this case, that block caches the
value that was looked up (i.e. C<
%var
{
$_
}>) in the
"previous"
slot and
returns it.
"Aha!"
you
say
, "that's a bug! What
if
the value of C<
%var
{
$_
}> is
false?!" Well,
if
it were possible
for
that to ever happen, then it
certainly I<would> be a bug, and we'd have to
write
something ugly:
when
defined
%var
{
$_
} {
return
%var
{
""
} =
%var
{
$_
} }
But, of course, it's much easier just to redefine Truth, so that any
literal zero value stored in C<
%var
> is
no
longer false. See below.
Finally,
if
the C<
$data
> isn't a literal, then a C<
"previous"
>, or a
variable name, it must be an invalid token, so the
default
alternative
in the switch statement throws an C<Err::BadData> exception:
default
{
die
Err::BadData :
msg
=>
"Don't understand $_"
}
Note that, here again, we are actually executing a method call to:
Err::BadData.
die
(
msg
=>
"Don't understand $_"
);
as indicated by the
use
of the colon
after
the classname.
Of course, by using C<
die
> instead of C<fail> here, we're giving
clients of the C<get_data> subroutine
no
choice but to deal
with
C<Err::BadData> exceptions.
=head1 An Aside: the
"Smart Match"
Operator
The rules governing how the argument of a C<
when
> is matched against
the current topic are designed to be as DWIMish as possible. Which
means that they are actually quite complex. They're listed in
Apocalypse 4, so we won't review them here.
Collectively, the rules are designed to provide a generic "best attempt
at matching" behavior. That is,
given
two
values
(the current topic and
the C<
when
>'s first argument), they
try
to determine whether those
values
can be combined to produce a
"smart match"
--
for
some
reasonable definitions of
"smart"
and
"match."
That means that one possible
use
of a Perl 6 switch statement is simply
to test I<whether> two
values
match without worrying about I<how> those
two
values
match:
sub
hey_just_see_if_dey_match_willya (
$val1
,
$val2
) {
given
$val1
{
when
$val2
{
return
1 }
default
{
return
0 }
}
}
That behavior is sufficiently useful that Larry wanted to make it much
easier to
use
. Specifically, he wanted to provide a generic "smart
match" operator.
So he did. It's called C<=~>.
Yes, the humble Perl 5
"match a string against a regex"
operator is
promoted in Perl 6 to a "smart-match an I<anything> against an
I<anything>" operator. So now:
if
(
$val1
=~
$val2
) {...}
works out the most appropriate way to compare its two
scalar
operands.
The result might be a numeric comparison (C<
$val1
==
$val2
>) or a
string comparison (C<
$val1
eq
$val2
>) or a subroutine call
(C<
$val1
.(
$val2
)>) or a pattern match (C<
$val1
=~ /
$val2
/>) or whatever
else
makes the most sense
for
the actual run-
time
types of the two
operands.
This new turbo-charged
"smart match"
operator will also work on arrays,
hashes and lists:
if
@array
=~
$elem
{...}
if
$key
=~
%hash
{...}
if
$value
=~ (1..10) {...}
if
$value
=~ (
'a'
,/\s/,7) {...}
That final example illustrates some of the extra intelligence that Perl
6's C<=~>
has
: When one of its arguments is a list (I<not> an array),
the
"smart match"
operator recursively
"smart matches"
each
element and
ORs the results together, short-circuiting
if
possible.
=head1 Being Calculating
The
next
component of the program is the subroutine that computes the
actual results of
each
expression that the user enters. It takes a
string to be evaluated and an integer indicating the current iteration
number of the main input loop (
for
debugging purposes):
sub
calc (str
$expr
,
int
$count
) {
=head1 Give us a little privacy, please
Perl 5
has
a really ugly idiom
for
creating
"durable"
lexical
variables: variables that are lexically scoped but stick
around
from
call to call.
If you
write
:
sub
whatever {
my
$count
if
0;
$count
++;
print
"whatever called $count times\n"
;
}
then the compile-
time
aspect of a C<
my
$count
> declaration causes
C<
$count
> to be declared as a lexical in the subroutine block. However,
at run-
time
--
when
the variable would normally be (re-)allocated --
the C<
if
0> prevents that process. So the original lexical variable is
not replaced on
each
invocation, and is instead shared by them all.
This awful C<
if
0> idiom works under most versions of Perl 5, but it's
really just a freakish accident of Perl's evolution, not a carefully
designed and lovingly crafted feature. So just
say
"No!"
.
Perl 6 allows us to
do
the same thing, but without feeling the need to
wash afterward.
To understand how Perl 6 cleans up this idiom, notice that the durable
variable is really much more; like a
package
variable that just happens
to be accessible only in a particular lexical scope. That kind of
restricted-access
package
variable is going to be quite common in Perl
6 -- as an attribute of a class.
So the way we create such a variable is to declare it as a
package
variable, but
with
the C<is private> property:
module Wherever;
sub
whatever {
our
$count
is private;
$count
++;
print
"whatever called $count times\n"
;
}
Adding C<is private> causes Perl to recognize the existence of the
variable C<
$count
> within the C<Wherever> module, but then to restrict
its accessibility to the lexical scope in which it is first declared.
In the above example, any attempt to refer to C<
$Wherever::count
>
outside the C<
&Wherever::whatever
> subroutine produces a compile-
time
error. It
's still a package variable, but now you can'
t
use
it anywhere
but in the nominated lexical scope.
Apart from the benefit of replacing an ugly hack
with
a clean explicit
marker on the variable, the real advantage is that Perl 6 private
variables can be also be initialized:
sub
whatever {
our
$count
is private //= 1;
print
"whatever called $count times\n"
;
$count
++;
}
That initialization is performed the first
time
the variable
declaration is encountered during execution (because that's the only
time
its value is C<
undef
>, so that's the only
time
the C<//=> operator
has
any effect).
In
our
example program we
use
that facility to
do
a one-
time
-only
initialization of a private
package
hash. That hash will then be used
as a (lexically restricted) look-up table to provide the
implementations
for
a set of operator symbols:
our
%operator
is private //= (
'*'
=> { $^a * $^b },
'/'
=> { $^a / $^b },
'~'
=> { ($^a + $^b) / 2 },
);
Each key of the hash is an operator symbol and the corresponding value
is an anonymous subroutine that implements the appropriate operation.
Note the
use
of the
"place-holder"
variables (C<$^a> and C<$^b>) to
implicitly specify the parameters of the closures.
Since all the data
for
the C<
%operator
> hash is constant, we could have
achieved a similar effect
with
:
my
%operator
is constant = (
'*'
=> { $^a * $^b },
'/'
=> { $^a / $^b },
'~'
=> { ($^a + $^b) / 2 },
);
Notionally this is quite different from the C<is private> version, in
that -- theoretically -- the lexical constant would be reconstructed
and reinitialized on
each
invocation of the C<calc> subroutine.
Although, in practice, we would expect the compiler to notice the
constant initializer and optimize the initialization out to
compile-
time
.
If the initializer had been a run-
time
expression, then the
C<is private> and C<is constant> versions would behave very
differently:
our
%operator
is private //= todays_ops();
my
%operator
is constant = todays_ops();
=head1 Let's Split!
We then have to
split
the input expression into (whitespace-delimited)
tokens, in order to parse and execute it. Since the calculator language
we're implementing is RPN, we need a stack to store data and interim
calculations:
my
@stack
;
We also need a counter to track the current token number (
for
error
messages):
my
$toknum
= 1;
Then we just
use
the standard C<
split
> built-in to break up the
expression string, and iterate through
each
of the resulting tokens
using a C<
for
> loop:
for
split
/\s+/,
$expr
->
$token
{
There are several important features to note in this C<
for
> loop. To
begin
with
, there are
no
parentheses
around
the list. In Perl 6, they
are not required (they're not needed
for
I<any> control structure),
though they are certainly still permissible:
for
(
split
/\s+/,
$expr
) ->
$token
{
More importantly, the declaration of the iterator variable (C<
$token
>)
is
no
longer to the left of the list:
for
my
$token
(
split
/\s+/,
$expr
) {
Instead, it is specified via a topical arrow to the right of the list.
By the way, somewhat surprisingly, the Perl 6 arrow operator I<isn't> a
binary operator. (Actually, neither is the Perl 5 arrow operator, but
that's not important right now.)
Even more surprisingly, what the Perl 6 arrow operator is, is a synonym
for
the declarator C<
sub
>. That's right, in Perl 6 you can declare an
anonymous subroutine like so:
$product_plus_one
= ->
$x
,
$y
{
$x
*$y
+ 1 };
The arrow behaves like an anonymous C<
sub
> declarator:
$product_plus_one
=
sub
(
$x
,
$y
) {
$x
*$y
+ 1 };
except that its parameter list doesn't
require
parentheses. That
implies:
=over
=item *
The Perl 6 C<
for
>, C<
while
>, C<
if
>, and C<
given
> statements
each
take
two arguments: an expression that controls them and a
subroutine/closure that they execute. Normally, that closure is just a
block (in Perl6 I<all> blocks are really closures):
for
1..10 {
print
}
but you can also be explicit:
for
1..10,
sub
{
print
}
or you can be pointed:
for
1..10 -> {
print
}
or referential:
for
1..10,
&some_sub
;
=item *
The variable
after
the arrow is effectively a lexical variable
confined to the scope of the following block (just as a subroutine
parameter is a lexical variable confined to the scope of the
subroutine block). Within the block, that lexical becomes an alias
for
the topic (just as a subroutine parameter becomes an alias
for
the
corresponding argument).
=item *
Topic variables created
with
the arrow notation are, by
default
,
read
-only aliases (because Perl 6 subroutine parameters are, by
default
,
read
-only aliases):
for
@list
->
$i
{
if
(
$cmd
=~
'incr'
) {
$i
++;
}
}
Note that the rule doesn't apply to the
default
topic (C<
$_
>), which is
given
special dispensation to be a modifiable alias (as in Perl 5).
=item *
If you want a named topic to be modifiable through its alias, then you
have to
say
so explicitly:
for
@list
->
$i
is rw {
if
(
$cmd
=~
'incr'
) {
$i
++;
}
}
=item *
Just as a subroutine can have more than one parameter, so too we can
specify more than one named iterator variable at a
time
:
for
%phonebook
.kv ->
$name
,
$number
{
print
"$name: $number\n"
}
Note that in Perl 6, a hash in a list context returns a list of pairs,
not the Perl 5-ish
"key, value, key, value, ..."
sequence. To get the
hash contents in that
format
, we have to call the hash's C<kv> method
explicitly.
What actually happens in this iteration (and, in fact, in all such
instances) is that the C<
for
> loop looks at the number of arguments its
closure takes and iterates that many elements at a
time
.
Note that C<
map
> and C<reduce> can
do
that too in Perl 6:
@list_of_powers
=
map
{ $^x ** $^y }
@xs_and_ys
;
$sum_of_powers
= reduce { $^partial_sum + $^x ** $^y } 0,
@xs_and_ys
;
And, of course, since C<
map
> and C<reduce> take a subroutine reference
as their first argument -- instead of using the higher-order
placeholder notation -- we could
use
the arrow notation here too:
@list_of_powers
=
map
->
$x
,
$y
{
$x
**
$y
}
@xs_and_ys
;
or even an old-fashioned anonymous subroutine:
@list_of_powers
=
map
sub
(
$x
,
$y
){
$x
**
$y
},
@xs_and_ys
;
=back
Phew. If that all makes your head hurt, then don't worry. All you
really need to remember is this: If you don't want to
use
C<
$_
> as the
name of the current topic, then you can change it by putting an arrow
and a variable name
before
the block of most control statements.
=head1 A Trying Situation
Once the calculator's input
has
been
split
into tokens, the C<
for
> loop
processes
each
one in turn, by applying them (
if
they represent an
operator), or jumping out of the loop (
if
they represent an
end-of-expression marker: C<
'.'
>, C<
';'
>, or C<
'='
>), or pushing them
onto the stack (since anything
else
must be an operand):
try
{
when
%operator
{
my
@args
=
splice
@stack
, -2;
push
@stack
,
%operator
{
$token
}(*
@args
);
}
when
'.'
,
';'
,
'='
{
last
;
}
push
@stack
, get_data(
$token
);
The first two possibilities are tested
for
using C<
when
> statements.
Recall that a C<
when
> tests its first argument against the current
topic. In this case, however, the token was made the topic by the
surrounding C<
for
>. This is a significant feature of Perl 6: C<
when
>
blocks can implement a switch statement I<anywhere> there is a valid
topic, not just inside a C<
given
>.
The block associated
with
C<
when
%operator
> will be selected
if
C<
%operator
{
$token
}> is true (i.e.
if
there is an operator
implementation in C<
%operator
> corresponding to the current topic). In
that case, the top two arguments are spliced from the stack and passed
to the closure implementing that operation
(C<
%operator
{
$token
}(*
@args
)>). Note that there would normally be a dot
(C<.>) operator between the hash entry (i.e. a subroutine reference)
and the subroutine call, like so:
%operator
{
$token
}.(*
@args
)
but in Perl 6 it may be omitted since it can be inferred (just as an
inferrable C<< -> >> can be omitted in Perl 5).
Note too that we used the flattening operator (C<*>) on C<
@args
>,
because the closure returned by C<
%operator
{
$token
}> expects two
scalar
arguments, not one array.
The second C<
when
> simply exits the loop
if
it finds an
"end-of-expression"
token. In this example, the argument of the C<
when
>
is a list of strings, so the C<
when
> succeeds
if
any of them matches
the token.
Of course, since the entire body of the C<
when
> block is a single
statement, we could also have written the C<
when
> as a statement
modifier:
last
when
'.'
,
';'
,
'='
;
The fact that C<
when
>
has
a postfix version like this should come as
no
surprise, since C<
when
> is simply another control structure like C<
if
>,
C<
for
>, C<
while
>, etc.
The postfix version of C<
when
> does have one interesting feature. Since
it governs a statement, rather than a block, it does not provide the
block-C<
when
>'s automatic "C<break> to the end of
my
topicalizing
block" behavior. In this instance, it makes
no
difference since the
C<
last
> would
do
that anyway.
The final alternative -- pushing the token onto the stack -- is simply
a regular Perl C<
push
> command. The only interesting feature is that it
calls the C<get_data> subroutine to pre-translate the token
if
necessary. It also specifies a C<
use
fatal> so that C<get_data> will
fail by an throwing exception, rather than returning C<
undef
>.
The loop tries
each
of these possibilities in turn. And
"tries"
is the
operative word here, because either the application of operations or
the pushing of data onto the stack may fail, resulting in an exception.
To prevent that exception from propagating all the way back to the main
program and terminating it, the various alternatives are placed in a
C<
try
> block.
A C<
try
> block is the Perl 6 successor to Perl 5's C<
eval
> block.
Unless it includes some explicit error handling code (see
L<
"Where's the Catch???"
>), it acts exactly like a Perl 5
C<
eval
{...}>, intercepting a propagating exception and converting it
to an C<
undef
>
return
value:
try
{
$quotient
=
$numerator
/
$denominator
} //
warn
"couldn't divide"
;
=head1 Where's the Catch???
In Perl 6, we aren't limited to just blindly catching a propagating
exception and then coping
with
an C<
undef
>. It is also possible to set
up an explicit handler to
catch
, identify and deal
with
various types
of exceptions. That's done in a C<CATCH> block:
CATCH {
when
Err::Reportable {
warn
$!;
continue
}
when
Err::BadData { $!.fail(
at
=>
$toknum
) }
when
NoData {
push
@stack
, 0 }
when
/division by zero/ {
push
@stack
, Inf }
}
A C<CATCH> block is like a C<BEGIN> block (hence the capitalization).
Its one argument is a closure that is executed
if
an exception ever
propagates as far as the block in which the C<CATCH> was declared. If
the block eventually executes, then the current topic is aliased to the
error variable C<$!>. So the typical thing to
do
is to populate the
exception handler's closure
with
a series of C<
when
> statements that
identify the exception contained in C<$!> and handle the error
appropriately. More on that L<in a moment|
"Catch as Catch Can"
>.
The C<CATCH> block
has
one additional property. When its closure
has
executed, it transfers control to the end of the block in which it was
defined
. This means that exception handling in Perl 6 is
non-resumptive: once an exception is handled, control passes outward,
and the code that threw the exception is not automatically re-executed.
If we did want
"try, try, try again"
exception handling instead, then
we
'd need to explicitly code a loop around the code we'
re trying:
sub
getnum_or_die {
given
<> {
die
"$_ is not a number"
unless
defined
&& /^\d+$/;
return
$_
;
}
}
sub
readnum_or_cry {
return
getnum_or_die;
CATCH {
warn
$! }
}
sub
readnum_or_retry {
loop {
return
getnum_or_die;
CATCH {
warn
$! }
}
}
Note that this isn't true resumptive exception handling. Control still
passes outward -- to the end of the C<loop> block. But then the C<loop>
reiterates, sending control back into C<getnum_or_die>
for
another
attempt.
=head1 Catch as Catch Can
Within the C<CATCH> block, the example uses the standard Perl 6
exception handling technique: a series of C<
when
> statements. Those
C<
when
> statements compare their arguments against the current topic.
In a C<CATCH> block, that topic is always aliased to the error variable
C<$!>, which contains a reference to the propagating exception object.
The first three C<
when
> statements
use
a classname as their argument.
When matching a classname against an object, the C<=~> operator (and
therefore any C<
when
> statement) will call the object's C<isa> method,
passing it the classname. So the first three cases of the handler:
when
Err::Reportable {
warn
$!;
continue
}
when
Err::BadData { $!.fail(
at
=>
$toknum
) }
when
NoData {
push
@stack
, 0 }
are (almost) equivalent to:
if
$!.isa(Err::Reportable) {
warn
$! }
elsif
$!.isa(Err::BadData) { $!.fail(
at
=>
$toknum
) }
elsif
$!.isa(NoData) {
push
@stack
, 0 }
except far more readable.
The first C<
when
> statement simply passes the exception object to
C<
warn
>. Since C<
warn
> takes a string as its argument, the exception
object's stringification operator (inherited from the standard
C<Exception> class) is invoked and returns an appropriate diagnostic
string, which is printed. The C<
when
> block then executes a C<
continue
>
statement, which circumvents the
default
"C<break> out of the
surrounding topicalizer block" semantics of the C<
when
>.
The second C<
when
> statement calls the propagating exception's C<fail>
method to cause C<calc> either to
return
or rethrow the exception,
depending on whether C<
use
fatal> was set. In addition, it passes some
extra information to the exception, namely the number of the token that
caused the problem.
The third C<
when
> statement handles the case where there is
no
cached
data corresponding to the calculator's C<
"previous"
> keyword, by simply
pushing a zero onto the stack.
The final case that the handler tests
for
:
when
/division by zero/ {
push
@stack
, Inf }
uses a regex, rather than a classname. This causes the topic (i.e. the
exception) to be stringified and pattern-matched against the regex. As
mentioned above, by
default
, all exceptions stringify to their own
diagnostic string. So this part of the handler simply tests whether
that string includes the words
"division by zero,"
in which case it
pushes the Perl 6 infinity value onto the stack.
=head1 One Dot Only
The C<CATCH> block handled bad data by calling the C<fail> method of
the current exception:
when
Err::BadData { $!.fail(
at
=>
$toknum
) }
That's a particular instance of a far more general activity: calling a
method on the current topic. Perl 6 provides a shortcut
for
that -- the
prefix unary dot operator. Unary dot calls the method that is its
single operand, using the current topic as the implicit invocant. So
the C<Err::BadData> handler could have been written:
when
Err::BadData { .fail(
at
=>
$toknum
) }
One of the main uses of unary dot is to allow C<
when
> statements to
select
behavior on the basis of method calls. For example:
given
$some_object
{
when
.has_data(
'new'
) {
print
"New data available\n"
}
when
.has_data(
'old'
) {
print
"Old data still available\n"
}
when
.is_updating {
sleep
1 }
when
.can(
'die'
) { .
die
(
"bad state"
) }
default
{
die
"internal error"
}
}
Unary dot is also useful within the definition of methods themselves.
In a Perl 6 method, the invocant (i.e. the first argument of the
method, which is a reference to the object on which the method was
invoked) is always the topic, so instead of writing:
method dogtag (Soldier
$self
) {
print
$self
.rank,
" "
,
$self
.name,
"\n"
unless
$self
.status(
'covert'
);
}
we can just
write
:
method dogtag (Soldier
$self
) {
print
.rank,
" "
, .name,
"\n"
unless
.status(
'covert'
);
}
or even just:
method dogtag {
print
.rank,
" "
, .name,
"\n"
unless
.status(
'covert'
);
}
Yet another
use
of unary dot is as a way of abbreviating multiple
accesses to hash or array elements. That is, C<
given
> also implements
the oft-coveted C<
with
> statement. If many elements of a hash or array
are to be accessed in a set of statements, then we can avoid the
tedious repetition of the container name:
$name
=
%options
{name} //
%options
{default_name};
$age
=
%options
{age};
$limit
= max(
%options
{limit},
%options
{rate} *
%options
{count});
$count
=
$limit
/
%options
{max_per_count};
by making it the topic and using unary dot:
given
%options
{
$name
= .{name} // .{default_name};
$age
= .{age};
$limit
= max(.{limit}, .{rate} * .{count});
$count
=
$limit
/ .{max_per_count};
}
=head1 Onward and Backward
Back in
our
example,
after
each
token
has
been dealt
with
in its loop
iteration, the iteration is finished. All that remains to
do
is
increment the token number.
In Perl 5, that would be done in a C<
continue
> block at the end of the
loop block. In Perl 6, it's done in a C<NEXT> statement I<within> the
loop block:
NEXT {
$toknum
++ }
Like a C<CATCH>, a C<NEXT> is a special-purpose C<BEGIN> block that
takes a closure as its single argument. The C<NEXT> pushes that closure
onto the end of a queue of
"next-iteration"
handlers, all of which are
executed
each
time
a loop reaches the end of an iteration. That is,
when
the loop reaches the end of its block or
when
it executes an
explicit C<
next
> or C<
last
>.
The advantage of moving from Perl 5
's external C<continue> to Perl 6'
s
internal C<NEXT> is that it gives the
"next-iteration"
handler access
to any lexical variables declared within the loop block. In addition,
it allows the
"next-iteration"
handler to be placed anywhere in the
loop that
's convenient (e.g. close to the initialization it'
s later
supposed to clean up).
For example, instead of having to
write
:
my
$in_file
,
$out_file
;
while
(<>) {
open
$in_file
,
$_
or
die
;
open
$out_file
,
"> $_.out"
or
die
;
}
continue
{
close
$in_file
or
die
;
close
$out_file
or
die
;
}
we can just
write
:
while
(<>) {
my
$in_file
=
open
$_
or
die
;
my
$out_file
=
open
"> $_.out"
or
die
;
NEXT {
close
$in_file
or
die
;
close
$out_file
or
die
;
}
}
There's
no
need to declare C<
$in_file
> and C<
$out_file
> outside the
loop, because they don't have to be accessible outside the loop (i.e.
in an external C<
continue
>).
This ability to declare, access and clean up lexicals within a
given
scope is especially important because, in Perl 6, there is
no
reference
counting to ensure that the filehandles
close
themselves automatically
immediately at the end of the block. Perl 6's full incremental garbage
collector I<does> guarantee to eventually call the filehandle's
destructors, but makes
no
promises about
when
that will happen.
Note that there is also a C<LAST> statement, which sets up a handler
that is called automatically
when
a block is left
for
the
last
time
.
For example, this:
for
reverse
1..10 {
print
"$_..."
and flush;
NEXT {
sleep
1 }
LAST { ignition() &&
print
"lift-off!\n"
}
}
prints:
10...9...8...7...6...5...4...3...2...1...lift-off!
sleeping one second
after
each
iteration (including the
last
one), and
then calling C<
&ignition
> at the end of the countdown.
C<LAST> statements are also extremely useful in nonlooping blocks, as a
way of giving the block a
"destructor"
with
which it can clean up its
state regardless of how it is exited:
sub
handler (
$value
,
$was_handled
is rw) {
given
$value
{
LAST {
$was_handled
= 1 }
when
&odd
{
return
"$value is odd"
}
when
/0$/ {
print
"decimal compatible"
}
when
/2$/ {
print
"binary compatible"
; break }
$value
%= 7;
when
1,3,5 {
die
"odd residual"
}
}
}
In the above example,
no
matter how the C<
given
> block exits -- i.e.
via the C<
return
> of the first C<
when
> block, or via the (implicit)
C<break> of the second C<
when
>, or via the (explicit and redundant)
C<break> of the third C<
when
>, or via the C<
"odd residual"
> exception,
or by falling off the end of the C<
given
> block -- the C<
$was_handled
>
parameter is always correctly set.
Note that the C<LAST> is essential here. It wouldn't suffice to
write
:
sub
handler (
$value
,
$was_handled
is rw) {
given
$value
{
when
&odd
{
return
'
$value
is odd" }
when
/3$/ {
print
"ternary compatible"
}
when
/2$/ {
print
"binary compatible"
; break }
$value
%= 7;
when
1,3,5 {
die
"odd residual"
}
}
$was_handled
= 1;
}
because then C<
$handled
> wouldn't be set
if
an exception was thrown. Of
course,
if
that
's actually the semantics you I<wanted>, then you don'
t
want C<LAST> in that case.
=head1 WHY ARE YOU SHOUTING???
You may be wondering why C<
try
> is in lower case but C<CATCH> is in
upper. Or why C<NEXT> and C<LAST> blocks have those
"loud"
keywords.
The reason is simple: C<CATCH>, C<NEXT> and C<LAST> blocks are just
specialized C<BEGIN> blocks that install particular types of handlers
into the block in which they appear.
They install those handlers at compile-
time
so, unlike a C<
try
> or a
C<
next
> or a C<
last
>, they don't actually I<
do
> anything
when
the
run-
time
flow of execution reaches them. The blocks associated
with
them are only executed
if
the appropriate condition or exception is
encountered within their scope. And,
if
that happens, then they are
executed automatically, just like C<AUTOLOAD>, or C<DESTROY>, or
C<TIEHASH>, or C<FETCH>, etc.
So Perl 6 is merely continuing the long Perl tradition of using a
capitalized keyword to highlight code that is executed automatically.
In other words: I'M SHOUTING BECAUSE I WANT YOU TO BE AWARE THAT
SOMETHING SUBTLE IS HAPPENING AT THIS POINT.
=head1 Cache and Return
Meanwhile, back in C<calc>...
Once the loop is complete and all the tokens have been processed, the
result of the calculation should be the top item on the stack. If the
stack of items
has
more than one element left, then it's likely that
the expression was wrong somehow (most probably, because there were too
many original operands). So we report that:
fail Err::BadData :
msg
=>
"Too many operands"
if
@stack
> 1;
If everything is OK, then we simply
pop
the one remaining value off the
stack and make sure it will evaluate true (even
if
its value is zero or
C<
undef
>) by setting its C<true> property. This avoids the potential
bug L<discussed earlier|
"Still Other Whens"
>.
Finally, we record it in C<
%var
> under the key C<
'$I<n>'
> (i.e. as the
I<n>-th result), and
return
it:
return
%var
{
'$'
_
$i
} =
pop
(
@stack
) but true;
"But, but, but..."
, I hear you expostulate, "...shouldn't that be
C<
pop
(
@stack
) B<is> true>???"
Once upon a
time
, yes. But Larry
has
recently decided that compile-
time
and run-
time
properties should have different keywords. Compile-
time
properties (i.e. those ascribed to declarations) will still be
specified
with
the C<is> keyword:
class Child is interface;
my
$heart
is constant =
"true"
;
our
$meeting
is private;
whereas run-
time
properties (i.e. those ascribed to
values
) will now be
specified
with
the C<but> keyword:
$str
= <
$trusted_fh
> but tainted(0);
$fh
=
open
(
$filename
) but chomped;
return
0 but true;
The choice of C<but> is meant to convey the fact that run-
time
properties will generally contradict some standard property of a value,
such as its normal truth, chompedness or tainting.
It's also meant to keep people from writing the very natural, but very
misguided:
if
(
$x
is true) {...}
which now generates a (compile-
time
) error:
Can't ascribe a compile-
time
property to the run-
time
value of
$x
.
(Did you mean
"$x but true"
or
"$x =~ true"
?)
=head1 The Forever Loop
Once the C<Calc> module
has
all its functionality
defined
, all that's
required is to
write
the main input-process-output loop. We'll cheat a
little and
write
it as an infinite loop, and then (in solemn Unix
tradition) we'll
require
an EOF signal to
exit
.
The infinite loop needs to keep track of its iteration count. In Perl 5
that would be:
for
(
my
$i
=0; 1;
$i
++) {
which would translate into Perl 6 as:
loop (
my
$i
=0; 1;
$i
++) {
since Perl 5's C-like C<
for
> loop
has
been renamed C<loop> in Perl 6 --
to distinguish it from the Perl-like C<
for
> loop.
However, Perl 6 also allows us to create semi-infinite, lazily
evaluated lists, so we can
write
the same loop much more cleanly as:
for
0..Inf ->
$i
{
When C<Inf> is used as the right-hand operand to C<..>, it signifies
that the resulting list must be lazily built, and endlessly iterable.
This type of loop will probably be common in Perl 6 as an easy way of
providing a loop counter.
If we need to iterate some list of
values
, as well as tracking a loop
counter, then we can take advantage of another new feature of Perl 6:
iteration streams.
A regular Perl 6 C<
for
> loop iterates a single stream of
values
,
aliasing the current topic to
each
in turn:
for
@stream
->
$topic_from_stream
{
...
}
But it's also possible to specify two (or more) streams of
values
that
the one C<
for
> loop will step through I<in parallel>:
for
@stream1
;
@stream2
->
$topic_from_stream1
;
$topic_from_stream2
{
...
}
Each stream of
values
is separated by a semicolon, and
each
topic
variable is similarly separated. The C<
for
> loop iterates both streams
in parallel, aliasing the
next
element of the first stream
(C<
@stream1
>) to the first topic (C<
$topic_from_stream1
>) and the
next
element of the second stream (C<
@stream2
>) to the second topic
(C<
$topic_from_stream2
>).
The commonest application of this will probably be to iterate a list
and simultaneously provide an iteration counter:
for
@list
; 0..
@list
.
last
->
$next
;
$index
{
print
"Element $index is $next\n"
;
}
It may be useful to set that out slightly differently, to show the
parallel nature of the iteration:
for
@list
; 0..
@list
.
last
->
$next
;
$index
{
print
"Element $index is $next\n"
;
}
It's important to note that writing:
for
@a
;
@b
->
$x
;
$y
{...}
is I<not> the same as writing:
for
@a
,
@b
->
$x
,
$y
{...}
The difference is that semicolons separate streams,
while
commas
separate elements within a single stream.
If we were brave enough, then we could even combine the two:
for
@a1
,
@a2
;
@b
->
$x
;
$y1
,
$y2
{...}
This is definitely a case where a different layout would help make the
various iterations and topic bindings clearer:
for
@a1
,
@a2
;
@b
->
$x
;
$y1
,
$y2
{...}
Note, however, that the normal way in Perl 6 to step through an array's
values
while
tracking its indices will almost certainly be to
use
the
array's C<kv> method. That method returns a list of interleaved indices
and
values
(much like the hash's C<kv> method returns alternating
keys
and
values
):
for
@list
.kv ->
$index
,
$next
{
print
"Element $index is $next\n"
;
}
=head1 Read or Die
Having prompted
for
the
next
expression that the calculator will
evaluate:
print
"$i> "
;
we
read
in the expression and check
for
an EOF (which will cause the
C<< <> >> operator to
return
C<
undef
>, in which case we escape the
infinite loop):
my
$expr
= <> err
last
;
Err...C<err>???
In Apocalypse 3, Larry introduced the C<//> operator, which is like a
C<||> that tests its left operand
for
definedness rather than truth.
What he didn't mention (but which you probably guessed) was that there
is also the low-precedence version of C<//>. Its name is C<err>:
Operation High Precedence Low Precedence
INCLUSIVE OR || or
EXCLUSIVE OR ~~ xor
DEFINED OR // err
But why call it C<err>?
Well, the C<//> operator looks like a skewed version of C<||>, so the
low-precedence version should probably be a skewed version of C<or>. We
can't skew it visually (even Larry thought that using italics would be
going a bit far), so we skew it phonetically instead:
C<or> -> C<err>.
C<err> also
has
the two handy mnemonic connotations:
=over
=item *
That we're handling an B<err>or marker (which a returned C<
undef
>
usually is)
=item *
That we're voicing a surprised double-take
after
something
unexpected (which a returned C<
undef
> often is).
=back
Besides all that, it just seems to work well. That is, something like
this:
my
$value
= compute_value(
@args
)
err
die
"Was expecting a defined value"
;
reads quite naturally in English (whether you think of C<err> as an
abbreviation of
"on error..."
, or as a synonym
for
"oops..."
).
Note that C<err> is a binary operator, just like C<or>, and C<xor>, so
there's
no
particular need to start it on a new line:
my
$value
= compute_value(
@args
) err
die
"Was expecting a defined value"
;
In
our
example program, the C<
undef
> returned by the C<< <> >>
operator at end-of-file is
our
signal to jump out of the main loop. To
accomplish that we simply append C<err
last
> to the input statement:
my
$expr
= <> err
last
;
Note that an C<or
last
> wouldn't work here, as both the empty string
and the string
"0"
are valid (i.e. non-terminating) inputs to the
calculator.
=head1 Just Do It
Then it's just a matter of calling C<Calc::calc>, passing it the
iteration number and the expression:
Calc::calc(
i
=>
$i
,
expr
=>
$expr
)
Note that we used named arguments, so passing them in the wrong order
didn't matter.
We then interpolate the result back into the output string using the
C<$(...)>
scalar
interpolator:
print
"$i> $( Calc::calc(i=>$i, expr=>$expr) )\n"
;
We could even simplify that a little further, by taking advatage of the
fact that subroutine calls interpolate directly into strings in Perl 6,
provided we
use
the C<&> prefix:
print
"$i> &Calc::calc(i=>$i, expr=>$expr)\n"
;
Either way, that
's it: we'
re done.
=head1 Summing Up
In terms of control structures, Perl 6:
=over
=item *
provides far more support
for
exceptions and exception handling,
=item *
cleans up and
extends
the C<
for
> loop syntax in several ways,
=item *
unifies the notions of blocks and closures and makes them interchangeable,
=item *
provides hooks
for
attaching various kinds of automatic handlers to a
block/closure,
=item *
re-factors the concept of a switch statement into two far more general ideas:
marking a value/variable as the current topic, and then doing
"smart matching"
against that topic.
=back
These extensions and cleanups offer us far more power and control, and
-- amazingly -- in most cases
require
far less syntax. For example,
here's (almost) the same program, written in Perl 5:
sub
warn
{
die
@_
}
my
%var
;
sub
get_data {
my
$data
=
shift
;
if
(
$data
=~ /^\d+$/) {
return
$var
{
""
} =
$data
}
elsif
(
$data
eq
'previous'
) {
return
defined
$var
{
""
}
?
$var
{
""
}
:
die
NoData->new()
}
elsif
(
$var
{
$data
}) {
return
$var
{
""
} =
$var
{
$data
} }
else
{
die
Err::BadData->new(
{
msg
=>
"Don't understand $data"
}
)
}
}
sub
calc {
my
%data
=
@_
;
my
(
$i
,
$expr
) =
@data
{
'i'
,
'expr'
};
my
%operator
= (
'*'
=>
sub
{
$_
[0] *
$_
[1] },
'/'
=>
sub
{
$_
[0] /
$_
[1] },
'~'
=>
sub
{ (
$_
[0] +
$_
[1]) / 2 },
);
my
@stack
;
my
$toknum
= 1;
LOOP:
for
my
$token
(
split
/\s+/,
$expr
) {
defined
eval
{
TRY:
if
(
$operator
{
$token
}) {
my
@args
=
splice
@stack
, -2;
push
@stack
,
$operator
{
$token
}->(
@args
);
last
TRY;
}
last
LOOP
if
$token
eq
'.'
||
$token
eq
';'
||
$token
eq
'='
;
push
@stack
, get_data(
$token
);
} ||
do
{
if
($@->isa(Err::Reportable)) {
warn
$@; }
if
($@->isa(Err::BadData)) { $@->{at} =
$i
;
die
$@ }
elsif
($@->isa(NoData)) {
push
@stack
, 0 }
elsif
($@ =~ /division by zero/) {
push
@stack
, ~0 }
}
}
continue
{
$toknum
++ }
die
Err::BadData->new(
msg
=>
"Too many operands"
)
if
@stack
> 1;
$var
{
'$'
.
$i
} =
$stack
[-1] .
' but true'
;
return
0+
pop
(
@stack
);
}
for
(
my
$i
=1; 1;
$i
++) {
print
"$i> "
;
defined
(
my
$expr
= <> ) or
last
;
print
"$i> ${\Calc::calc(i=>$i, expr=>$expr)}\n"
;
}
Hmmmmmmm. I know which version I<I'd> rather maintain.