The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Contextual::Return::Wrapper - Common functionality using wantarray

SYNOPSIS

  use parent qw( Contextual::Return::Wrapper Exporter ) ;

  __PACKAGE__->import ;

  sub formalize {
        ## baseline function without attribute

        return map { "Mr. $_" } @_ ;
        }

  sub uppercase : Listify {
        return map { uc } @_ ;
        }
  
  sub lowercase : ReturnContext( scalar => 'first' ) {
        return map { lc } @_ ;
        }

  my $sir = formalize( 'John' ) ;               ## $sir =: 1
  my $john = uppercase( qw( John ) ) ;          ## $john =: JOHN
  my $jim = lowercase( qw( Jim John ) ) ;       ## $jim =: jim

ReturnContext takes a variety of qualifiers which are listed below.

DESCRIPTION

The functions shown in the "SYNOPSIS" can be generally referred to as list mutators. When the return list is evaluated in a scalar context, as shown in the formalize() example above, the result is not what's usually expected.

Contextual::Return::Wrapper automatically wraps these functions to change this behavior using attributes to specify pre-defined functionality.

Listify

Several users responded with the following recommendation when I posted my original request:

  sub _listify {
        return @_[0..$#_] ;
        }

  sub calculate_distance {
        my ( $origin_zipcode, @remote_zipcodes ) = @_ ;
        ## Latitude/Longitude lookups and calculations
        return _listify( @distances ) ;
        }

Contextual::Return::Wrapper delivers the same effect:

  sub calculate_distance : Listify {
        my ( $origin_zipcode, @remote_zipcodes ) = @_ ;
        ## Latitude/Longitude lookups and calculations
        return @distances ;
        }

The primary advantage is a common, standardized solution with a somewhat cleaner interface.

Note potential confusion with the identically named Scalar::Listify module. The "Listify" attribute is intended to convert a list to a scalar, while the module converts a scalar to a list.

ReturnContext

A more general approach to this problem involves using the wantarray operator to determine the calling context of a function. For example, the Contextual::Return module provides an alternative to wantarray that provides more finely grained definitions. Contextual::Return::Wrapper also provides an alternative to explicit wantarray calls by using a wrapper to implement predefined functionality.

Obviously, no one would bother with anything so trivial, but the over-simplified lowercase() function demonstrates where functionality is added by the wrapper:

requires qualifiers: array, scalar, void

  sub lowercase : ReturnContext( requires => 'array' ) {
        return map { lc } $_ ;
        }
  ## $single= lowercase( qw( Jim John ) ) => generates warning
  ## lowercase( qw( Jim John ) ) => generates warning


  sub lowercase : ReturnContext( requires => 'scalar' ) {
        return map { lc } $_ ;
        }
  ## @list = lowercase( qw( Jim John ) ) => generates warning
  ## lowercase( qw( Jim John ) ) => generates warning


  sub lowercase : ReturnContext( requires => 'void' ) {
        return map { lc } $_ ;
        }
  ## @list = lowercase( qw( Jim John ) ) => generates warning
  ## $single= lowercase( qw( Jim John ) ) => generates warning

scalar qualifiers: first, last, count, array_ref, warn

  sub lowercase : ReturnContext( scalar => 'first' ) {
        return map { lc } $_ ;
        }
  ## scalar lowercase( qw( Jim John ) ) := 'jim'


  sub lowercase : ReturnContext( scalar => 'last' ) {
        return map { lc } $_ ;
        }
  ## scalar lowercase( qw( Jim John ) ) := 'john'


  sub lowercase : ReturnContext( scalar => 'count' ) {
        return map { lc } $_ ;
        }
  ## scalar lowercase( qw( Jim John ) ) := 2


  sub lowercase : ReturnContext( scalar => 'array_ref' ) {
        return map { lc } $_ ;
        }
  ## lowercase( qw( Jim John ) )->[0] := 'jim'


  sub lowercase : ReturnContext( scalar => 'warn' ) {
        return map { lc } $_ ;
        }
  ## scalar lowercase( qw( Jim John ) ) => generates warning

void qualifiers: warn

  sub lowercase : ReturnContext( void => 'warn' ) {
        return map { lc } $_ ;
        }
  ## lowercase( qw( Jim John ) ) => generates warning

combined qualifiers

Qualifiers can be combined as follows:

  sub lowercase : ReturnContext( void => 'warn', scalar => 'first' ) {
        return map { lc } $_ ;
        }

CAVEATS

evaluate.t

Some attribute definitions occur during compile time, which can cause problems for run-time evalution environments such as modperl. Wrapping these compile-time definitions inside the import() definition seems like a successful work-around.

In order to use these attributes inside a local package (eg main), import() needs to be explicitly invoked as shown in the "SYNOPSIS".

  __PACKAGE__->import ;

exporter.t

The extended import() function is going to conflict with Exporter::import(), so the inheritance tree needs to be carefully defined. The Exporter class should be last, at the root, as follows:

  use parent qw( Contextual::Return::Wrapper Exporter ) ;

It seems that, like DESTROY() destructors, some provision needs to chain import() calls through an inheritance tree. The export() function may have potential as a general-purpose solution. It can be used in any package with the following invocation:

  goto &Contextual::Return::Wrapper::export

SEE ALSO

  • The module Attribute::Context addresses the same problems.

  • If this modules doesn't solve your problem, then Contextual::Return probably does.

  • Scalar::Listify also addresses a similar problem domain.

  • Attribute::Handler is a good resource for designing attribute based interfaces. I adopted its style of defining attribute qualifiers similar to function arguments.

DBIx::Class provides an example of the problem:

  @rows = $resultset->search( { id => { '<', 20 } } ) ; 
  $row = $resultset->search( { id => 20 } ) ;
  ## DBIx::Class automatically performs the list => scalar conversion

Please email me directly for questions, comments, suggestions, and other feedback. The pre-defined functionality may be incomplete.

ACKNOWLEDGEMENTS

Admittedly, technically, I got in a little over my head. My original intention was to publish a straightforward solution to standardize a variety of hacks and idioms. The following individuals provided helpful instruction:

  • Curtis Ovid Poe

  • Rolf Langsdorf

  • Aristotle Pagaltzis

  • Linda Walsh

  • Chicago Perlmongers

AUTHOR

Jim Schueler, <jim@tqis.com>

COPYRIGHT AND LICENSE

Copyright (C) 2013 by Jim Schueler

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.9 or, at your option, any later version of Perl 5 you may have available.