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.