Properly forward return value in Perl - perl

I'm pretty new to Perl and have a hard time wrapping my head around its very implicit type-system. What I like to achieve is a simple wrapper function that has the same signature as the function it wraps so it can be used in its place.
So let's say I have an existing function orig that I want to wrap. I takes a number of input arguments and has different return types based on those arguments. As long as I write the wrapper in the following way the return types are the same as the original function and it all works nicely:
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
return orig(#_);
}
However, if I want to execute some code in the wrapper after the execution of orig I don't know how I preserve the types. From my understanding, the same way the input to a perl function is always an array of scalars so is the output. So the solution then should be this:
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
my #result = orig(#_);
print "after";
return #result;
}
But that does not seem to work as expected. What am I missing? How to write such wrapper function that it works properly for arbitrary return types?

From my understanding, the same way the input to a perl function is
always an array of scalars so is the output.
No, not quite.
Perl functions can be called in list context, scalar context, or void context.
some_function(#args); # void
my $result = some_function(#args); # scalar
my #results = some_function(#args); # list
Many of Perl's built-in functions act differently according to which context they are called. For example grep returns a list of results in list context, and a count of results in scalar context.
If you are writing your own function and wish to behave differently in different contexts, the function can use the wantarray keyword to detect what context it's been called in. wantarray returns true for list context, false for scalar context, and undef for void context.
Even if you are not consciously wanting to write a function which behaves differently according to context, you may end up doing so accidentally by returning a context-sensitive expression such as grep, or map, or an array (arrays in scalar context return their length).
The correct way to wrap a function without breaking context is this. And yes, I realise it ain't pretty.
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
my #result =
wantarray ? orig(#_) :
defined wantarray ? scalar orig(#_) :
do { orig(#_); () };
print "after";
wantarray ? #result : $result[0];
}
Now, if your wrapper doesn't need to alter #_, and doesn't need to alter the return values, then Class::Method::Modifiers can make this a bit easier:
use Class::Method::Modifiers;
sub wrapper { orig(#_) } # do nothing in the wrapper itself
before wrapper => sub {
my ($first) = #_;
print "before. first argument: $first\n";
};
after wrapper => sub {
print "after";
};

I suspect there's a better way to do what you want in Perl, but since you specify only the mechanism I can tell you only how to make it work.
The return value of a Perl subroutine depends on the context of the call. Within the subroutine you can use the built-in operator wantarray to detect the context -- it will return a true value if the call was in list context, and false otherwise.
So to pass on the value that another subroutine would return in the same context, you would have to write wrapper like this
sub wrapper {
my ($first) = #_;
print "before. first argument: $first\n";
if (wantarray) {
my #result = orig(#_);
print "after";
return #result;
}
else {
my $result = orig(#_);
print "after";
return $result;
}
}
But bear in mind that a given subroutine is probably meant to be called in either scalar or list context. Writing something that behaves differently according to context is rare, so you would normally want only one of these two conditional branches depending on the behaviour of orig.

Related

overloading a Perl object to redirect hash access to custom routine

A while ago, I wrote a routine that parses a given string and returns a record in the form of a hash (field => value, field2 => value2). Great, except requirements have changed and I now need to return more data and offer getter methods to get at this data. So, I adjusted the routine to return a Record object which stores this same hash in a data attribute.
However, this will break legacy code that expects a hash so that it can get at the data using $record->{field}. With the new Record object, the path to this data is now $record->{data}->{field} or $record->getByShortName('field').
My idea was to overload the object's FETCH method and return the corresponding field. However, this does not seem to work. It looks like FETCH is never called.
I'm looking for three pieces of advice:
How can I overload my object correctly so that hash access attempts are redirected to a custom method of the object?
Is this an advisable way of working or will there be a massive speed penalty?
Are there better methods to keep backward compatibility in my case?
Here's an MVE:
Record.pm
package Record;
use strict;
use warnings;
use Data::Dumper;
use overload fallback => 1, '%{}' => \&access_hash;
sub new {
my ($class, %args) = #_;
my %fields = (answer => 42, question => 21);
$args{fields} = \%fields;
return bless { %args }, $class;
}
sub access_hash {
my ($self) = shift;
return $self; # cannot return $self->{fields} because that would recurse ad infinitum
}
sub FETCH {
print(Dumper(#_)); # does not return anything, is this method not being called
}
test.pl
use Record;
my $inst = Record->new();
print($inst->{answer}."\n");
print($inst->{question}."\n");
Record is a blessed hash reference, so if you overload the %{} operator, you will have trouble accessing the fields of the underlying hash.
The overload authors thought about this, and provided the overloading pragma as a way to disable overloading for this and some other use cases.
use overload '%{}' => \&access_hash;
...
sub access_hash {
no overloading '%{}';
my ($self) = shift;
return $self->{fields};
}
Prior to Perl 5.10, the workaround was to disable overloading by temporarily reblessing your object to something that wouldn't activate your overloaded operators.
sub access_hash {
my ($self) = shift;
my $orig_ref = ref($self);
bless $self, "#$%^&*()";
my $fields = $self->{fields};
bless $self, $orig_ref;
return $fields;
}
You don't neccessarily need a dedicated constructor for Perl objects. You can define your Record class, and then simply return bless $hashref, 'Record'; where you are now doing return $hashref;. All code that operates directly on the hashref will continue to work, but you will also be able to call methods on it.

Not enough arguments when redefining a subroutine

When I redefine my own subroutine (and not a Perl built-in function), as below :
perl -ce 'sub a($$$){} sub b {a(#_)}'
I get this error :
Not enough arguments for main::a at -e line 1, near "#_)"
I'm wondering why.
Edit :
The word "redefine" is maybe not well chosen. But in my case (and I probably should have explained what I was trying to do originally), I want to redefine (and here "redefine" makes sense) the Test::More::is function by printing first Date and Time before the test result.
Here's what I've done :
Test::More.pm :
sub is ($$;$) {
my $tb = Test::More->builder;
return $tb->is_eq(#_);
}
MyModule.pm :
sub is ($$;$) {
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is(#_);
}
The prototype that you have given your subroutine (copied from Test::More::is) says that your subroutine requires two mandatory parameters and one optional one. Passing in a single array will not satisfy that prototype - it is seen as a single parameter which will be evaluated in scalar context.
The fix is to retrieve the two (or three) parameters passed to your subroutine and to pass them, individually, to Test::More::is.
sub is ($$;$) {
my ($got, $expected, $test_name) = #_;
my $t = gmtime(time);
my $date = $t->ymd('/').' '.$t->hms.' ';
print($date);
Test::More::is($got, $expected, $test_name);
}
The problem has nothing to do with your use of a prototype or the fact that you are redefining a subroutine (which, strictly, you aren't as the two subroutines are in different packages) but it's because Test::More::is() has a prototype.
You are not redefining anything here.
You've set a prototype for your sub a by saying sub a($$$). The dollar signs in the function definition tell Perl that this sub has exactly three scalar parameters. When you call it with a(#_), Perl doesn't know how many elements will be in that list, thus it doesn't know how many arguments the call will have, and fails at compile time.
Don't mess with prototypes. You probably don't need them.
Instead, if you know your sub will need three arguments, explicitly grab them where you call it.
sub a($$$) {
...
}
sub b {
my ($one, $two, $three) = #_;
a($one, $two, $three);
}
Or better, don't use the prototype at all.
Also, a and b are terrible names. Don't use them.
In Perl, prototypes don't validate arguments so much as alter parsing rules. $$;$ means the sub expects the caller to match is(EXPR, EXPR) or is(EXPR, EXPR, EXPR).
In this case, bypassing the prototype is ideal.
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is(#_);
}
Since you don't care if Test::More::is modifies yours #_, the following is a simple optimization:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
return &Test::More::is;
}
If Test::More::is uses caller, you'll find the following useful:
sub is($$;$) {
print gmtime->strftime("%Y/%m/%d %H:%M:%S ");
goto &Test::More::is;
}

Perl function: accept hash, array, or scalar as parameter

I have the following sub-routine.
sub Function{
my $ref = \($_[0]);
if(ref($ref) eq 'SCALAR'){
. . .
}
}
I am trying to get it to work such that passing a list, scalar or hash to it, it converts that into a reference and depending upon whether that reference is of type ARRAY, SCALAR or HASH, different actions are performed.
It could be called as
Function(%a)
Function($a)
Function(#a)
How can I make $ref be a reference to whatever is passed to the Function? My current approach isn't working.
Function(%a)
Function($a)
Function(#a)
You can't do what you are asking. There is no way inside Function to determine whether an array or a hash was passed, because, in fact, you can't pass an array or a hash to a sub in the first place. You can only pass a list; the hashes and arrays are converted to lists in the sub calls above.
You should pass a reference to the function in the first place:
Function(\%a)
Function($a)
Function(\#a)
Then you can check what it is easily:
sub Function {
my $param = shift;
if (not ref $param) {
...
} elsif (ref $param eq 'HASH') {
...
} elsif (ref $param eq 'ARRAY') {
...
}
You can use a prototyped function. Although Perl 5 prototypes are a total mess, they can be used quite well in this case. As I understand them the prototype
sub Function (\[$#%]) {...}
should solve your problem, and the reference be in $_[0].
The big disadvantage is that the variable you pass has to start with the $, #, or % character, so you can't call Function with a constant argument directly:
Function(1)
fails, but
my $temp = 1;
Function($temp)
works.
Your approach failes, because #_ is a list, and all elements are scalars.
Why not just check to see if the argument passed in is a reference and act accordingly?
sub gimme_a_ref
{
my $arg=shift;
if(ref $arg ne ref "")
{
return $arg;
}
else
{
return \$arg;
}
}

perl function ignores undef arguments

I'm passing some arguments to a function, one of which might be undefined.
$a = ($config->function(param('a'),'int'));
my module contains a function which looks like this:
sub function{
my $self = $_[0];
my $t = $_[1];
my $type = $_[2];
print "$self,$t,$type<br/>";
}
I've tried with shift instead of the #_ syntax, but there's no change.
The problem is that if $config->function is called with an undefined param('a') it prints like this:
MY_FUNC=HASH(0x206e9e0),name,
it seems that $t is being set to the value of what $type should be and the undef is being ignored completely.
undef is perfectly valid in a parameter list. I suspect that the problem here is that the param function is returning an empty list, rather than undef.
sub function {
no warnings 'uninitialized';
print join "/", #_;
}
function(undef,"foo"); # outputs "/foo"
function((), "foo"); # outputs "foo"
In the argument list to function, the param function is evaluated in list context.
sub param1 {
return; # undef in scalar context, but empty list in list context
}
sub param2 {
return undef; # scalar ctx: undef, list ctx: list with one undef elem
}
function(param1(), "foo"); # param1() -> () ... outputs "foo"
function(param2(), "foo"); # param2() -> (undef) ... outputs "/foo"
A workaround is to make sure that your param function is evaluated in scalar context.
function(scalar param1(), "foo"); # now outputs "/foo"
Note that actually saying return undef in your Perl subroutines is considered by some to be a code smell.
The CGI param method is correctly returning an empty list in the case of an error. This is standard behaviour for Perl subroutines and methods, which return a false value in the case of an erorr.
As #mob says, an explicit return undef is wrong precisely because it returns a one-element list in list context. This is a true value and implies success, which would break a lot of code if it was changed
All you need to do is change your code to extract the parameter separately
my $param = param('a');
$config->function($param, 'int'));
or if you're really keen to put the call in the parameter list then you can write
$config->function(scalar param('a'), 'int'));
or
$config->function(param('a') // undef, 'int'));
[I started this as a comment but it got too long].
Inside an argument list, arguments are evaluated in list context. Two answers have already noted that, but I think you missed it for all of the other things they were talking about.
Beyond that, param has to handle at least three cases, and the interface Lincoln chose, although sometimes annoying, is a good enough way to handle them.
The query parameter a does not appear at all:
b=1
The query parameter a appears, but with no value:
a=&b=1
The query parameter a appears with a value:
a=1&b=1

What's the difference between return; and return undef; in Perl

Is there a difference between a subroutine that does
return;
and one that does?
return undef;
return; will return an empty list in list context but undef in scalar context. return undef; will always return a single value undef even in list context.
In general, it's usually not a good idea to return undef; from a subroutine normally used in list context:
sub foo { return undef }
if ( my #x = foo() ) {
print "oops, we think we got a result";
}
In general, it's usually not a good idea to return; from a subroutine normally used in scalar context, because it won't behave as the user expects in list context:
sub foo { return }
%x = ( 'foo' => foo(), 'bar' => 'baz' );
if ( ! exists $x{'bar'} ) {
print "oops, bar became a value, not a key";
}
Both of these errors happen quite a bit in practice, the latter more so, perhaps because subs that are expected to return a scalar are more common. And if it's expected to return a scalar, it had better return a scalar.
Given
sub foo { return; }
sub bar { return undef; }
In scalar context, they behave the same.
my $foo = foo(); # $foo is undef
my $bar = bar(); # $bar is undef
In list context, they behave differently
my #foo = foo(); # #foo is () (an empty list)
my #bar = bar(); # #bar is ( undef ) (a one-element list)
Note that a one-element list is a true value in boolean context, even though the only element is undef.
In general, it's usually not a good idea to return undef; from a subroutine, because of how it behaves in context.
I think I still agree with PBP though.
1) You should avoid nesting function calls:
my $result = bar();
foo(1, $result, 2);
2) You should always be explicit (when doing "complicated" things):
foo(1, scalar bar(), 2);
The book "Perl Best Practices" recommends the use of return; instead of return undef; because the latter would return a one-element list in list context (the other answers already mention this). This would be a "nasty bug" according to the book.
However, I think returning nothing can actually cause serious bugs (probably difficult to find), whereas calling a boolean function in list context seems rather simple to debug.
So I always return something (that evaluates to) false in my boolean functions (usually 0 for false and 1 for true).
(Yes, this would also return a one-element list in list context - so the comment in the book is technically right -, but then the actual error would be calling the boolean function in list context.)
To be clear, I still recommend the book. It offers a lot of good advice.
And I am referring to Chapter 9 (Subroutines), page 199/200 ("Use a bare return to return failure.") in the book "Perl Best Practices" by Damian Conway (O'Reilly Media, Inc.), ISBN 978-0-596-00173-5. Not sure if there's another/newer edition.
Side note:
Perl seems to return an empty string when negating something. my $foo = 5; return !$foo; returns q().