NAME
Perl6::Contexts - array and hash variables turn into references to themselves when used in non-numeric scalar context or as function arguments
SYNOPSIS
my @foo = ( 1 .. 20 );
my $foo = @foo; # same as my $foo = \@foo;
my $foo = 0 + @foo; # unchanged - length of @foo
$obj->some_method(10, 20, @foo); # same as $obj->some_method(10, 20, \@foo);
some_function(10, 20, @foo); # same as some_function(10, 20, \@foo);
DESCRIPTION
Perl6::Contexts makes Perl 5 behave more like Perl 6 with reguard to the meaning of array and hash variables when used in scalar context and function calls.
Using this module to make Perl 5 more like Perl 6 won't go very far towards writing Perl 5 that will run under Perl 6 but it will help you get used to some of the changes. (As far as legacy code is concerned, check out the excellent PONIE project - everyones money is on PONIE!).
This module doesn't add new syntax - it merely changes the meaning of existing syntax.
Context
Perl 6 divides scalar context into boolean, numeric, string, and object context, among others.
Reference Context
Arrays and hashes used in reference context turn into a reference to themselves. We assume reference context unless we know better. This vaguely approximates Perl 6's behavior. For example, given a completely spurrious my $foo = @bar
, we assume that $foo
should be a reference to @bar
.
Numeric Context
Arrays used in numeric context return their size, as in Perl 5. Perl 6 uses the +
prefix or num
, int
, or float
keywords to force numeric context. We don't have those keywords (yet), but +
and scalar
do the trick for now. Numeric context is also supplied by math related operators such as -
, *
, sin
, and so on.
Force numeric context to get the old Perl 5 behavior of counting the elements in an array or hash:
scalar @arr;
0 + @arr;
In Perl 6, the 0
is redundant and undesireably ugly but it is required for our purposes so I suggest using scalar
instead.
Note that hashes return internal memory allocation information when used in scalar context - use scalar keys %hash
to count the number of items it contains.
Boolean Context
Boolean context formalizes the murky semantics of "zero but true" for Perl 6 but our implementation doesn't do anything to help with that. Our boolean context is currently identical to Perl 5's scalar context which is identical to numeric context and is provided by and
, or
, &&
, ||
, and other conditionls.
String Context
Perl 6 gives arrays, hashes, and objects, among other things, control over how they present themselves when used as a string. Perl 6 adds interpolation of hashes in quoted text, along with the arrays and scalars that already interpolate in Perl 5. Each variable can be extended with a trait to control the exact details of its presentation. Perl 5 allows a minimal amount of presentation control with the global $"
variable. See perldoc perlvar's entry on $"
for details. We don't try to interpolate hashes in strings but we do join
on $"
to stringify arrays when used as a string. The .
operator, for example, forces string context.
use Perl6::Contexts;
my $t1; my $t2; my $t3;
my @arr = ( 1 .. 20 );
print '@arr: ' . @arr . "\n"; # note that . is used instead of comma
.
forces string context on @arr
in this example.
Or:
use Perl6::Contexts;
my $t1; my $t2; my $t3;
my @arr = ( 1 .. 20 );
$" = '-';
@arr =~ m/15-16/ or die;
=~
forces string context on @arr
in this example. That's a lot more useful than matching on a string representing of the number of things in @arr
.
Yes, the my $t1
things are needed to use arrays in string context. It's a long story. See the BUGS section for details if you're curious but it's a limitation I hope to overcome soon. There must be one such variable allocated for each string context use of an array in the single most complex expression in the module (and thus is the sacrifice that must be paid homage to satisify the demons that make this module work).
Context Summary
This module cheats a bit in guessing context. Contexts do not propogate (yet) as they do in Perl. Operators such as ||
do not yet apply the context to their operands that they themselves got from somewhere. The point of some contexts, such as boolean, is entirely missed. In general, the Perl 6 rules and this module come closer to the ideal of "do what I mean".
Function Calls
Hashes and arrays as function and method call arguments don't flatten by default. Perl 6 uses the splat operator, *
, to flatten arrays and hashes sent as arguents to functions. Like Perl 6, we don't flatten implicitly either. Unlike Perl 6, explicit flattening is kind of painful.
use Perl6::Contexts;
my @numbers = map int rand 100, 1 .. 100;
sub_that_wants_a_bunch_of_numbers(@numbers); # passes by reference - wrong
sub_that_wants_a_bunch_of_numbers(\@numbers); # same thing - wrong
In order to flatten things for subroutines that actually want flattened arrays, use one of these tricks:
sub_that_wants_a_bunch_of_numbers(@numbers[0 .. $#numbers]);
sub_that_wants_a_bunch_of_numbers(@numbers->flatten());
->flatten()
requires autobox. See below. Perl 6's *
operator, which forcefully unflattens arrays, is not available in Perl 5 or via this module.
Subroutines called by code subjected to the rules of Perl6::Contexts must accept references to arrays and hashes unless the array or hash in the call to that subroutine was explicitly flattened:
use Perl6::Contexts;
my @array = ( 1 .. 20 );
sub_that_wants_an_array_ref(@array);
sub sub_that_wants_an_array_ref {
my $arrayref = shift; # @array turned into a reference
my @array = @$arrayref; # or use an autobox trick if you like
}
This applies even if the subroutine or method is in another package entirely. Note that the requirement that @$arrayref
be written that way and not $arrayref
is an incompleteness of this module though obviously we aren't going to munge modules that don't use us. See the autobox tricks below and of course $arrayref
may be used directly as the array reference that it is.
autobox Interopation
This module works with autobox. Normally autobox requires a reference, a scalar, a number, a string, or a code reference, which excludes arrays and hashes:
use autobox;
use autobox::Core;
my @arr = ( 1 .. 20);
@arr->sum->print; # doesn't work without Perl6::Contexts
(\@arr)->sum->print; # works without Perl6::Contexts but ugly
Same goes for hashes. (While this is a fluke side effect of what we're doing I was aware of the consequence early on and it was a great motiviation to create this module, so autobox integration is a feature beyond any doubt.)
Often you'll want arrays and hashes to flatten when passed as arguments:
use Perl6::Contexts;
my @numbers = map int rand 100, 1 .. 100;
sub_that_wants_a_bunch_of_numbers(@numbers); # passes by reference - wrong
autobox and autobox::Core may be used to force array flattening:
use Perl6::Contexts;
use autobox;
use autobox::Core;
my @numbers = map int rand 100, 1 .. 100;
sub_that_wants_a_bunch_of_numbers(@numbers->flatten); # explicit flattening
To accomplish this without autobox, you may take a slice of the entire array:
use Perl6::Contexts;
my @numbers = map int rand 100, 1 .. 100;
sub_that_wants_a_bunch_of_numbers(@numbers[0 .. $#numbers]); # ugly but works
BUGS
There are no meaningful texts at this time. That's on the top of the list for 0.2. Consider this a preview release.
autobox::Core makes assumptions about what Perl 6 will name autoboxed methods on primtive types. As I learn more (and more is published) these examples and autobox::Core will change to be consistent with Perl 6. In other words, do not rely on the interface staying the same until version 1.0 (and then you're taking your own chances but I'll try my best).
Scalar variables used in conditionals (such as if
and and
) don't dereference themselves and reference values are always true (unless you do something special). Hence this will always die:
use Perl6::Contexts;
my @arr = ( ); # completely empty arrays evaluate false
my $arrref = @arr; # takes a reference
die if $arrref; # always dies - ERROR
You must use autobox
and autobox::Core
and write die if $arrref->flatten()
, or else write the old Perl 5 stand by, @$arrref
.
push
, pop
, exists
, delete
, shift
, unshift
, sort
, map
, join
, and grep
issue compile time warnings when used on a scalar even though this scalar could only possibly be a reference.
push $arrref, 1;
# diagnostic: Type of arg 1 to push must be array (not scalar dereference)
Perl 6 handles this correctly. Perl 5 could with replacement versions of those statements written in Perl. Perhaps in the next version this module will. Of course, it would be nice if the core did the "right thing" ;)
The unary *
operator doesn't flatten lists as it does in Perl 6. Instead, autobox and ->flatten
must be used for this, or synonymously, ->elements
.
scalar
is considered to provide numeric context. This is not consistent with Perl 6, where string
, bool
, bit
, string
, int
, num
, and float
generate contexts, much like scalar
does in Perl 5. This module should export those keywords.
While 0 + @arr
accidentally works to put @arr
in numeric context and get its length, no unary ~
(yet) exists to force string context (though it could - it would mean no more negating strings full of bits without calling a function in another module to do it).
my @array = $arrayref
should, but doesn't, dereference $arrayref
and dump its contents into @array
. This can, and should, be done but I haven't gotten to it yet.
Hashes in strings should interpolate but that's outside the scope of this module. See Perl6::Interpolators for an implementation.
Making users create temporaries is a kludge as ugly as any. I plan to roll this ability into B::Generate. Why are my $t1
, my $t2
, and so on, required? Perl associates nameless lexical variables with operations to speed up the stack machine. Each operation has its own virtually private scalar value, array value, hash value, or so on, that it can push to the stack any time it likes without having to allocate it. Next time the instruction runs again it knows that it can reuse the same variable. B::Generate isn't able to allocate these for instructions so I have to use preexisting named variables.
HISTORY
0.3 Fixes a serious bug where only the first of any number of arrays or hashes passed to a subroutine would referencify. The logic to loop through through the bytecode couldn't handle the bytecode changing out from under it and it lost its place. Added several todo list items to the top of the file for myself and those curious about possible future development.
0.2 Fixes a show stopper bug that broke autobox
and method calls, where the same array or hash would referencify twice. Code with anonymous subroutines triggered a fatal bug.
Versions fixing bugs I've found and adding features I think of will increment the minor version number. 1.0 will be released after a sufficient amount of user feedback suggestions that I'm not as far off in la-la land as I might be for all I know. This la-la land caveat applies to the Perl 6 specification as well, which I am doubtlessly botching.
SEE ALSO
autobox associates methods with primitive types allowing more complex APIs for types than would be reasonable to create built-in functions for. autoboxing also simplifies complex expressions that would require a lot of parenthesis by allowing the expression to be arranged into more a logical structure.
autobox::Core compliments autobox with wrappers for most built-in functions, some statements, some functionalish methods from core modules, and some Perl 6-ish things.
Perl 6 is able to take $arrayref[0]
to mean $arrayref.[0]
which is $arrayref->[0]
in Perl 5. This module won't get you that but see Perl6::Variables.
Want gives Perl 5 subroutines Perl 6-like information about the context they execute in, including the number of result values expected, boolean context, BOOL
, and various kinds of reference contexts. It is a generalized replacement for the built-in wantarray function.
B represents Perl internal data structures (including and especially bytecode instructions for the virtual machine) as Perl objects within perl itself. B::Generate extends B with the capability to modify this bytecode from within the running program (!!!). This module uses these two modules to do what it does. Opcode served as a reference, and code was stolen from B::Utils, B::Deparse, and B::Concise (but with implicit permission - yes, Free Software programmers do steal but never uninvited - seriously, I owe a debt of gratitude to those whose work I've built on, especially Simon Cozens and Malcolm Beattie in this case).
http://perldesignpatterns.com/?PerlAssembly attempts to document the Perl internals I'm prodding so bluntly.
AUTHOR
SWALTERS, scott@slowass.net