Scott Walters > Perl6-Contexts-0.4 > Perl6::Contexts

Download:
Perl6-Contexts-0.4.tar.gz

Dependencies

Annotate this POD

View/Report Bugs
Module Version: 0.4   Source  

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 regard to the array and hash variables as used as arguments to operators, method calls, and functions.

This module doesn't add new syntax -- it merely changes the meaning of existing syntax. 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.

To run legacy Perl 5 along side Perl 6, check out PONIE or Inline::Pugs.

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 ^

Most of these bugs are fixable but why should I bother if no one is actually using this module? Want a bug fixes? Email me. A little encouragement goes a long way.

Until I get around to finishing reworking B::Generate, B::Generate-1.06 needs line 940 of B-Generate-1.06/lib/B/Generate.c changed to read o = Perl_fold_constants(o); (the word Perl and an understore should be inserted). This is in order to build B::Generate-1.06 on newer Perls.

.. and ... aren't yet recognized numeric operators.

@arr = ( @arr2, @arr3, @arr4 ) should not flatten (I think) but currently does.

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. As far as I know, this is unfixable without resorting to a source filter, which I won't do in this module.

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, but doesn't, 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

syntax highlighting: